Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2578 lines
74 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module ime.cpp -- support for Win95 IME API |
  5. *
  6. * Most everything to do with FE composition string editing passes
  7. * through here.
  8. *
  9. * Authors: <nl>
  10. * Jon Matousek <nl>
  11. * Hon Wah Chan <nl>
  12. * Justin Voskuhl <nl>
  13. *
  14. * History: <nl>
  15. * 10/18/1995 jonmat Cleaned up level 2 code and converted it into
  16. * a class hierarchy supporting level 3.
  17. *
  18. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  19. */
  20. #include "_common.h"
  21. #ifndef NOFEPROCESSING
  22. #include "msctf.h"
  23. #include "textserv.h"
  24. #include "_cmsgflt.h"
  25. #include "_ime.h"
  26. #include "imeapp.h"
  27. #define HAVE_COMPOSITION_STRING() ( 0 != (lparam & (GCS_COMPSTR | GCS_COMPATTR)))
  28. #define CLEANUP_COMPOSITION_STRING() ( 0 == lparam )
  29. #define HAVE_RESULT_STRING() ( 0 != (lparam & GCS_RESULTSTR))
  30. ASSERTDATA
  31. /*
  32. * HRESULT StartCompositionGlue (CTextMsgFilter &TextMsgFilter)
  33. *
  34. * @func
  35. * Initiates an IME composition string edit.
  36. * @comm
  37. * Called from the message loop to handle WM_IME_STARTCOMPOSITION.
  38. * This is a glue routine into the IME object hierarchy.
  39. *
  40. * @devnote
  41. * We decide if we are going to do a level 2 or level 3 IME
  42. * composition string edit. Currently, the only reason to
  43. * create a level 2 IME is if the IME has a special UI, or it is
  44. * a "near caret" IME, such as the ones found in PRC and Taiwan.
  45. * Near caret simply means that a very small window opens up
  46. * near the caret, but not on or at the caret.
  47. *
  48. * @rdesc
  49. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  50. */
  51. HRESULT StartCompositionGlue (
  52. CTextMsgFilter &TextMsgFilter) // @parm containing message filter.
  53. {
  54. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "StartCompositionGlue");
  55. if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->IsTerminated()
  56. && !TextMsgFilter._ime->_compMessageRefCount && !(TextMsgFilter._fHangulToHanja))
  57. {
  58. delete TextMsgFilter._ime;
  59. TextMsgFilter._ime = NULL;
  60. }
  61. if(!TextMsgFilter.IsIMEComposition())
  62. {
  63. if(TextMsgFilter._pTextSel->CanEdit(NULL) == NOERROR &&
  64. !TextMsgFilter.NoIMEProcess())
  65. {
  66. // Hold notification if needed
  67. if (!(TextMsgFilter._fIMEAlwaysNotify))
  68. TextMsgFilter._pTextDoc->SetNotificationMode(tomFalse);
  69. // If a special UI, or IME is "near caret", then drop into lev. 2 mode.
  70. DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM);
  71. // use Unicode if not running under Win95
  72. TextMsgFilter._fUnicodeIME =
  73. (imeProperties & IME_PROP_UNICODE) && !W32->OnWin95();
  74. if ((imeProperties & IME_PROP_SPECIAL_UI) ||
  75. !(imeProperties & IME_PROP_AT_CARET))
  76. {
  77. TextMsgFilter._ime = new CIme_Lev2(TextMsgFilter); // level 2 IME.
  78. }
  79. else
  80. TextMsgFilter._ime = new CIme_Lev3(TextMsgFilter); // level 3 IME->TrueInline.
  81. }
  82. else // Protect or read-only or NOFEPROCESSING:
  83. TextMsgFilter._ime = new CIme_Protected; // Ignore all ime input
  84. }
  85. else
  86. {
  87. // Ignore further StartCompositionMsg.
  88. // Hanin 5.1 CHT symbol could cause multiple StartCompoisitonMsg.
  89. return S_OK;
  90. }
  91. if(TextMsgFilter.IsIMEComposition())
  92. {
  93. long lSelFlags;
  94. HRESULT hResult;
  95. hResult = TextMsgFilter._pTextSel->GetFlags(&lSelFlags);
  96. if (hResult == NOERROR)
  97. {
  98. TextMsgFilter._fOvertypeMode = !!(lSelFlags & tomSelOvertype);
  99. if (TextMsgFilter._fOvertypeMode)
  100. TextMsgFilter._pTextSel->SetFlags(lSelFlags & ~tomSelOvertype); // Turn off overtype mode
  101. }
  102. TextMsgFilter._pTextDoc->IMEInProgress(tomTrue); // Inform client IME compostion in progress
  103. return TextMsgFilter._ime->StartComposition(TextMsgFilter); // Make the method call.
  104. }
  105. else
  106. TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue);
  107. return S_FALSE;
  108. }
  109. /*
  110. * HRESULT CompositionStringGlue (const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  111. *
  112. * @func
  113. * Handle all intermediary and final composition strings.
  114. *
  115. * @comm
  116. * Called from the message loop to handle WM_IME_COMPOSITION.
  117. * This is a glue routine into the IME object hierarchy.
  118. * We may be called independently of a WM_IME_STARTCOMPOSITION
  119. * message, in which case we return S_FALSE to allow the
  120. * DefWindowProc to return WM_IME_CHAR messages.
  121. *
  122. * @devnote
  123. * Side Effect: the _ime object may be deleted if composition
  124. * string processing is finished.
  125. *
  126. * @rdesc
  127. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  128. */
  129. HRESULT CompositionStringGlue (
  130. const LPARAM lparam, // @parm associated with message.
  131. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  132. {
  133. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CompositionStringGlue");
  134. HRESULT hr = S_FALSE;
  135. if(TextMsgFilter.IsIMEComposition()) // A priori fHaveIMMProcs.
  136. {
  137. TextMsgFilter._ime->_compMessageRefCount++; // For proper deletion.
  138. // Make the method call.
  139. hr = TextMsgFilter._ime->CompositionString(lparam, TextMsgFilter);
  140. TextMsgFilter._ime->_compMessageRefCount--; // For proper deletion.
  141. Assert (TextMsgFilter._ime->_compMessageRefCount >= 0);
  142. CheckDestroyIME (TextMsgFilter); // Finished processing?
  143. }
  144. else // Even when not in composition mode, we may receive a result string.
  145. {
  146. DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM);
  147. long lSelFlags;
  148. HRESULT hResult;
  149. long cpMin, cpMax;
  150. hResult = TextMsgFilter._pTextSel->GetFlags(&lSelFlags);
  151. if (hResult == NOERROR)
  152. {
  153. TextMsgFilter._fOvertypeMode = !!(lSelFlags & tomSelOvertype);
  154. if (TextMsgFilter._fOvertypeMode)
  155. TextMsgFilter._pTextSel->SetFlags(lSelFlags & ~tomSelOvertype); // Turn off overtype mode
  156. }
  157. // Use Unicode if not running under Win95
  158. TextMsgFilter._fUnicodeIME =
  159. (imeProperties & IME_PROP_UNICODE) && !W32->OnWin95();
  160. TextMsgFilter._pTextSel->GetStart(&cpMin);
  161. TextMsgFilter._pTextSel->GetEnd(&cpMax);
  162. if (cpMin != cpMax)
  163. TextMsgFilter._pTextSel->SetText(NULL); // Delete current selection
  164. CIme::CheckKeyboardFontMatching (cpMin, &TextMsgFilter, NULL);
  165. TextMsgFilter._pTextDoc->IMEInProgress(tomTrue); // Inform client IME compostion in progress
  166. hr = CIme::CheckInsertResultString(lparam, TextMsgFilter);
  167. if(TextMsgFilter._fOvertypeMode)
  168. TextMsgFilter._pTextSel->SetFlags(lSelFlags | tomSelOvertype); // Turn on overtype mode
  169. TextMsgFilter._pTextDoc->IMEInProgress(tomFalse); // Inform client IME compostion is done
  170. }
  171. return hr;
  172. }
  173. /*
  174. * HRESULT EndCompositionGlue (CTextMsgFilter &TextMsgFilter, BOOL fForceDelete)
  175. *
  176. * @func
  177. * Composition string processing is about to end.
  178. *
  179. * @comm
  180. * Called from the message loop to handle WM_IME_ENDCOMPOSITION.
  181. * This is a glue routine into the IME object hierarchy.
  182. *
  183. * @devnote
  184. * The only time we have to handle WM_IME_ENDCOMPOSITION is when the
  185. * user changes input method during typing. For such case, we will get
  186. * a WM_IME_ENDCOMPOSITION message without getting a WM_IME_COMPOSITION
  187. * message with GCS_RESULTSTR later. So, we will call CompositionStringGlue
  188. * with GCS_RESULTSTR to let CompositionString to get rid of the string.
  189. *
  190. * @rdesc
  191. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  192. */
  193. HRESULT EndCompositionGlue (
  194. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  195. BOOL fForceDelete) // @parm forec to terminate
  196. {
  197. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "EndCompositionGlue");
  198. if(TextMsgFilter.IsIMEComposition())
  199. {
  200. // ignore the EndComposition message if necessary. We may
  201. // get this from 3rd party IME - EGBRIGDE after we have received
  202. // both result and composition strings.
  203. if ( !(TextMsgFilter._ime->_fIgnoreEndComposition) )
  204. {
  205. // Set this flag. If we are still in composition mode, then
  206. // let the CompositionStringGlue() to destroy the ime object.
  207. TextMsgFilter._ime->_fDestroy = TRUE;
  208. if (!fForceDelete)
  209. CompositionStringGlue(GCS_COMPSTR , TextMsgFilter); // Remove any remaining composition string.
  210. // Finished with IME, destroy it.
  211. CheckDestroyIME(TextMsgFilter);
  212. // Turn on undo
  213. TextMsgFilter._pTextDoc->Undo(tomResume, NULL);
  214. // Inform client IME compostion is done
  215. TextMsgFilter._pTextDoc->IMEInProgress(tomFalse);
  216. }
  217. else
  218. {
  219. // reset this so we will handle next EndComp msg
  220. TextMsgFilter._ime->_fIgnoreEndComposition = FALSE;
  221. }
  222. if(!TextMsgFilter.IsIMEComposition() && TextMsgFilter._fOvertypeMode)
  223. {
  224. long lSelFlags;
  225. HRESULT hResult;
  226. ITextSelection *pLocalTextSel = TextMsgFilter._pTextSel;
  227. BOOL fRelease = FALSE;
  228. if (!pLocalTextSel)
  229. {
  230. // Get the selection
  231. TextMsgFilter._pTextDoc->GetSelectionEx(&pLocalTextSel);
  232. fRelease = TRUE;
  233. }
  234. if (pLocalTextSel)
  235. {
  236. hResult = pLocalTextSel->GetFlags(&lSelFlags);
  237. if (hResult == NOERROR)
  238. pLocalTextSel->SetFlags(lSelFlags | tomSelOvertype); // Turn on overtype mode
  239. if (fRelease)
  240. pLocalTextSel->Release();
  241. }
  242. }
  243. }
  244. return S_FALSE;
  245. }
  246. /*
  247. * HIMC LocalGetImmContext ( CTextMsgFilter &TextMsgFilter )
  248. *
  249. * @func
  250. * Get Imm Context from host
  251. *
  252. */
  253. HIMC LocalGetImmContext(
  254. CTextMsgFilter &TextMsgFilter)
  255. {
  256. //TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "LocalGetImmContext");
  257. HIMC hIMC = NULL; // Host's IME context.
  258. HRESULT hResult;
  259. hResult = TextMsgFilter._pTextDoc->GetImmContext((long *)&hIMC);
  260. if (hResult != NOERROR)
  261. hIMC = ImmGetContext(TextMsgFilter._hwnd, TextMsgFilter._fUsingAIMM); // Get host's IME context.
  262. return hIMC;
  263. }
  264. /*
  265. * void LocalReleaseImmContext ( CTextMsgFilter &TextMsgFilter, HIMC hIMC )
  266. *
  267. * @func
  268. * call host to Release Imm Context
  269. *
  270. */
  271. void LocalReleaseImmContext(
  272. CTextMsgFilter &TextMsgFilter,
  273. HIMC hIMC )
  274. {
  275. //TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "LocalReleaseImmContext");
  276. HRESULT hResult;
  277. hResult = TextMsgFilter._pTextDoc->ReleaseImmContext((long)hIMC);
  278. if (hResult != NOERROR)
  279. ImmReleaseContext(TextMsgFilter._hwnd, hIMC, TextMsgFilter._fUsingAIMM);
  280. }
  281. /*
  282. * long IMEShareToTomUL ( UINT ulID )
  283. *
  284. * @func
  285. * Convert IMEShare underline to Tom underline.
  286. *
  287. * @rdesc
  288. * Tom underline value
  289. */
  290. long IMEShareToTomUL (
  291. UINT ulID )
  292. {
  293. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEShareToTomUL");
  294. long lTomUnderline;
  295. switch (ulID)
  296. {
  297. case IMESTY_UL_NONE:
  298. lTomUnderline = tomNone;
  299. break;
  300. case IMESTY_UL_DOTTED:
  301. lTomUnderline = tomDotted;
  302. break;
  303. case IMESTY_UL_THICK:
  304. case IMESTY_UL_THICKLOWER:
  305. lTomUnderline = tomThick;
  306. break;
  307. case IMESTY_UL_DITHLOWER:
  308. case IMESTY_UL_THICKDITHLOWER:
  309. lTomUnderline = tomWave;
  310. break;
  311. // case IMESTY_UL_SINGLE:
  312. // case IMESTY_UL_LOWER:
  313. default:
  314. lTomUnderline = tomSingle;
  315. break;
  316. }
  317. return lTomUnderline;
  318. }
  319. /*
  320. * void IMEMessage (CTextMsgFilter &TextMsgFilter , UINT uMsg, BOOL bPostMessage)
  321. *
  322. * @func
  323. * Either post or send message to IME
  324. *
  325. */
  326. BOOL IMEMessage(
  327. CTextMsgFilter &TextMsgFilter,
  328. UINT uMsg,
  329. WPARAM wParam,
  330. LPARAM lParam,
  331. BOOL bPostMessage)
  332. {
  333. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEMessage");
  334. HIMC hIMC; // Host's IME context.
  335. HWND hwndIME;
  336. BOOL fRetCode = FALSE;
  337. HWND hHostWnd = TextMsgFilter._hwnd;
  338. long hWnd;
  339. if (!hHostWnd) // Windowless mode...
  340. {
  341. if (TextMsgFilter._pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd)
  342. return FALSE;
  343. hHostWnd = (HWND)(DWORD_PTR)hWnd;
  344. }
  345. hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  346. if(hIMC)
  347. {
  348. hwndIME = ImmGetDefaultIMEWnd(hHostWnd, TextMsgFilter._fUsingAIMM);
  349. LocalReleaseImmContext(TextMsgFilter, hIMC);
  350. // check if we want to send or post message
  351. if (hwndIME)
  352. {
  353. if (bPostMessage)
  354. fRetCode = PostMessage(hwndIME, uMsg, wParam, lParam);
  355. else
  356. fRetCode = SendMessage(hwndIME, uMsg, wParam, lParam);
  357. }
  358. }
  359. return fRetCode;
  360. }
  361. /*
  362. * void CheckDestroyIME (CTextMsgFilter &TextMsgFilter)
  363. *
  364. * @func
  365. * Check for IME and see detroy if it needs it..
  366. *
  367. */
  368. void CheckDestroyIME (
  369. CTextMsgFilter &TextMsgFilter)
  370. {
  371. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CheckDestroyIME");
  372. if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->_fDestroy)
  373. {
  374. if(0 == TextMsgFilter._ime->_compMessageRefCount)
  375. {
  376. if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)
  377. {
  378. TextMsgFilter._pTextDoc->SetCaretType(tomNormalCaret); // Reset Block caret mode
  379. TextMsgFilter._fHangulToHanja = FALSE; // Reset korean conversion mode
  380. }
  381. delete TextMsgFilter._ime; // All done with object.
  382. TextMsgFilter._ime = NULL;
  383. TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue); // Turn on Notification
  384. }
  385. }
  386. }
  387. /*
  388. * void PostIMECharGlue (CTextMsgFilter &TextMsgFilter)
  389. *
  390. * @func
  391. * Called after processing a single WM_IME_CHAR in order to
  392. * update the position of the IME's composition window. This
  393. * is glue code to call the CIME virtual equivalent.
  394. */
  395. void PostIMECharGlue (
  396. CTextMsgFilter &TextMsgFilter) // @parm containing message filter.
  397. {
  398. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "PostIMECharGlue");
  399. if(TextMsgFilter.IsIMEComposition())
  400. TextMsgFilter._ime->PostIMEChar(TextMsgFilter);
  401. }
  402. /*
  403. * BOOL IMEMouseCheck(CTextMsgFilter &TextMsgFilter, UINT *pmsg,
  404. * WPARAM *pwparam, LPARAM *plparam, LRESULT *plres)
  405. *
  406. * @func
  407. * Called when receiving a mouse event. Need to pass this event
  408. * to MSIME98 for composition handling
  409. *
  410. */
  411. HRESULT IMEMouseCheck(
  412. CTextMsgFilter &TextMsgFilter, // @parm MsgFilter
  413. UINT *pmsg, // @parm the message
  414. WPARAM *pwparam, // @parm WParam
  415. LPARAM *plparam, // @parm LParam
  416. LRESULT *plres) // @parm Lresult
  417. {
  418. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEMouseCheck");
  419. BOOL fRetCode = FALSE;
  420. if(TextMsgFilter.IsIMEComposition())
  421. {
  422. BOOL fTerminateIME;
  423. fRetCode = TextMsgFilter._ime->IMEMouseOperation(TextMsgFilter, *pmsg, *pwparam, fTerminateIME);
  424. if ( fTerminateIME && WM_MOUSEMOVE != *pmsg )
  425. TextMsgFilter._ime->TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_NORMAL);
  426. }
  427. return fRetCode ? S_OK : S_FALSE;
  428. }
  429. /*
  430. * HRESULT IMENotifyGlue (const WPARAM wparam, const LPARAM lparam,
  431. * CTextMsgFilter &TextMsgFilter)
  432. *
  433. * @func
  434. * IME is going to change some state.
  435. *
  436. * @comm
  437. * Currently we are interested in knowing when the candidate
  438. * window is about to be opened.
  439. *
  440. * @rdesc
  441. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  442. */
  443. HRESULT IMENotifyGlue (
  444. const WPARAM wparam, // @parm associated with message.
  445. const LPARAM lparam, // @parm associated with message.
  446. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  447. {
  448. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMENotifyGlue");
  449. if (TextMsgFilter._fRE10Mode &&
  450. (wparam == IMN_SETCONVERSIONMODE ||
  451. wparam == IMN_SETSENTENCEMODE ||
  452. wparam == IMN_SETOPENSTATUS))
  453. {
  454. TextMsgFilter._pTextDoc->Notify(EN_IMECHANGE);
  455. }
  456. else if(TextMsgFilter.IsIMEComposition()) // A priori fHaveIMMProcs.
  457. return TextMsgFilter._ime->IMENotify(wparam, lparam, TextMsgFilter, FALSE);// Make the method call
  458. return S_FALSE;
  459. }
  460. /*
  461. * void IMECompositionFull (&TextMsgFilter)
  462. *
  463. * @func
  464. * Current IME Composition window is full.
  465. *
  466. * @comm
  467. * Called from the message loop to handle WM_IME_COMPOSITIONFULL.
  468. * This message applied to Level 2 only. We will use the default
  469. * IME Composition window.
  470. */
  471. void IMECompositionFull (
  472. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  473. {
  474. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMECompositionFull");
  475. if(TextMsgFilter.IsIMEComposition())
  476. {
  477. HIMC hIMC = LocalGetImmContext(TextMsgFilter);
  478. COMPOSITIONFORM cf;
  479. if(hIMC)
  480. {
  481. // No room for text input in the current level 2 IME window,
  482. // fall back to use the default IME window for input.
  483. cf.dwStyle = CFS_DEFAULT;
  484. ImmSetCompositionWindow(hIMC, &cf, TextMsgFilter._fUsingAIMM); // Set composition window.
  485. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  486. }
  487. }
  488. }
  489. /*
  490. * LRESULT OnGetIMECompositionMode (CTextMsgFilter &TextMsgFilter)
  491. *
  492. * @mfunc
  493. * Returns whether or not IME composition is being handled by RE,
  494. * and if so, what level of processing.
  495. *
  496. * @rdesc
  497. * One of ICM_NOTOPEN, ICM_LEVEL2_5, ICM_LEVEL2_SUI, ICM_LEVEL2, ICM_LEVEL3.
  498. */
  499. LRESULT OnGetIMECompositionMode (
  500. CTextMsgFilter &TextMsgFilter) // @parm containing message filter.
  501. {
  502. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "OnGetIMECompositionMode");
  503. if(TextMsgFilter.IsIMEComposition())
  504. return TextMsgFilter._ime->GetIMECompositionMode(TextMsgFilter);
  505. return ICM_NOTOPEN;
  506. }
  507. /*
  508. * LRESULT TestPoint (&pt1, &pt2, &ptTest, lTestOption, lTextFlow)
  509. *
  510. * @mfunc
  511. * Returns which side the ptTest is relative to the line (pt1, pt2) based on the lTextFlow.
  512. *
  513. * @rdesc
  514. * Sides detected
  515. */
  516. LONG TestPoint(
  517. POINT &pt1,
  518. POINT &pt2,
  519. POINT &ptTest,
  520. LONG lTestOption,
  521. LONG lTextFlow)
  522. {
  523. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "TestPoint");
  524. LONG lSidesDetect = 0;
  525. switch (lTextFlow)
  526. {
  527. case tomTextFlowES:
  528. if (lTestOption & TEST_LEFT)
  529. lSidesDetect |= (ptTest.x < pt1.x) ? TEST_LEFT : 0;
  530. if (lTestOption & TEST_RIGHT)
  531. lSidesDetect |= (ptTest.x > pt1.x) ? TEST_RIGHT : 0;
  532. if (lTestOption & TEST_TOP)
  533. lSidesDetect |= (ptTest.y < pt1.y) ? TEST_TOP : 0;
  534. if (lTestOption & TEST_BOTTOM)
  535. lSidesDetect |= (ptTest.y > pt2.y) ? TEST_BOTTOM : 0;
  536. break;
  537. case tomTextFlowSW:
  538. if (lTestOption & TEST_LEFT)
  539. lSidesDetect |= (ptTest.y < pt1.y) ? TEST_LEFT : 0;
  540. if (lTestOption & TEST_RIGHT)
  541. lSidesDetect |= (ptTest.y > pt1.y) ? TEST_RIGHT : 0;
  542. if (lTestOption & TEST_TOP)
  543. lSidesDetect |= (ptTest.x > pt1.x) ? TEST_TOP : 0;
  544. if (lTestOption & TEST_BOTTOM)
  545. lSidesDetect |= (ptTest.x < pt2.x) ? TEST_BOTTOM : 0;
  546. break;
  547. case tomTextFlowWN:
  548. if (lTestOption & TEST_LEFT)
  549. lSidesDetect |= (ptTest.x > pt1.x) ? TEST_LEFT : 0;
  550. if (lTestOption & TEST_RIGHT)
  551. lSidesDetect |= (ptTest.x < pt1.x) ? TEST_RIGHT : 0;
  552. if (lTestOption & TEST_TOP)
  553. lSidesDetect |= (ptTest.y > pt1.y) ? TEST_TOP : 0;
  554. if (lTestOption & TEST_BOTTOM)
  555. lSidesDetect |= (ptTest.y < pt2.y) ? TEST_BOTTOM : 0;
  556. break;
  557. case tomTextFlowNE:
  558. if (lTestOption & TEST_LEFT)
  559. lSidesDetect |= (ptTest.y > pt1.y) ? TEST_LEFT : 0;
  560. if (lTestOption & TEST_RIGHT)
  561. lSidesDetect |= (ptTest.y < pt1.y) ? TEST_RIGHT : 0;
  562. if (lTestOption & TEST_TOP)
  563. lSidesDetect |= (ptTest.x < pt1.x) ? TEST_TOP : 0;
  564. if (lTestOption & TEST_BOTTOM)
  565. lSidesDetect |= (ptTest.x > pt2.x) ? TEST_BOTTOM : 0;
  566. break;
  567. }
  568. return lSidesDetect;
  569. }
  570. /*
  571. * void CIme::CheckKeyboardFontMatching (long cp, CTextMsgFilter &TextMsgFilter, ITextFont *pTextFont)
  572. *
  573. * @mfunc
  574. * Setup current font to matches the keyboard Codepage.
  575. *
  576. * @comm
  577. * Called from CIme_Lev2::CIme_Lev2 and CompositionStringGlue
  578. *
  579. * @devnote
  580. * We need to switch to a preferred font for the keyboard during IME input.
  581. * Otherwise, we will display garbage.
  582. *
  583. */
  584. void CIme::CheckKeyboardFontMatching (
  585. long cp,
  586. CTextMsgFilter *pTextMsgFilter,
  587. ITextFont *pTextFont)
  588. {
  589. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::CheckKeyboardFontMatching");
  590. long lPitchAndFamily;
  591. HRESULT hResult;
  592. BSTR bstr = NULL;
  593. long lValue;
  594. long lNewFontSize=0;
  595. float nFontSize;
  596. ITextFont *pLocalFont = NULL;
  597. if (!pTextMsgFilter)
  598. return;
  599. if (!pTextFont)
  600. {
  601. // No font supplied, get current font from selection
  602. hResult = pTextMsgFilter->_pTextSel->GetFont(&pLocalFont);
  603. if (hResult != S_OK || !pLocalFont) // Can't get font, forget it
  604. return;
  605. pTextFont = pLocalFont;
  606. }
  607. // Check if current font matches the keyboard
  608. lValue = tomCharset;
  609. hResult = pTextFont->GetLanguageID(&lValue);
  610. BYTE bCharSet = (BYTE)lValue;
  611. BYTE bCharSetKB = GetCharSet(pTextMsgFilter->_uKeyBoardCodePage);
  612. if (hResult == S_OK && bCharSet == bCharSetKB)
  613. goto Exit; // Current font is fine
  614. hResult = pTextFont->GetSize(&nFontSize);
  615. if (hResult != S_OK)
  616. goto Exit;
  617. hResult = pTextMsgFilter->_pTextDoc->GetPreferredFont(cp,
  618. pTextMsgFilter->_uKeyBoardCodePage, tomMatchFontCharset,
  619. CodePageFromCharRep(CharRepFromCharSet(bCharSet)), (long)nFontSize,
  620. &bstr, &lPitchAndFamily, &lNewFontSize);
  621. if (hResult == S_OK)
  622. {
  623. pTextFont->Reset(tomApplyLater);
  624. if (bstr)
  625. pTextFont->SetName(bstr);
  626. // Set the font charset and Pitch&Family by overloading the SetLanguageID i/f
  627. lValue = tomCharset + ((BYTE)lPitchAndFamily << 8) + bCharSetKB;
  628. pTextFont->SetLanguageID(lValue);
  629. if (lNewFontSize)
  630. pTextFont->SetSize((float)lNewFontSize);
  631. pTextFont->Reset(tomApplyNow);
  632. }
  633. Exit:
  634. if (pLocalFont)
  635. pLocalFont->Release();
  636. if (bstr)
  637. SysFreeString(bstr);
  638. }
  639. /*
  640. * INT CIme::GetCompositionStringInfo(HIMC hIMC, DWORD dwIndex,
  641. * WCHAR *szCompStr, INT cchMax, BYTE *attrib, INT cbAttrib
  642. * LONG cchAttrib, UINT kbCodePage, BOOL bUnicodeIME)
  643. *
  644. * @mfunc
  645. * For WM_IME_COMPOSITION string processing to get the requested
  646. * composition string, by type, and convert it to Unicode.
  647. *
  648. * @devnote
  649. * We must use ImmGetCompositionStringA because W is not supported
  650. * on Win95.
  651. *
  652. * @rdesc
  653. * INT-cch of the Unicode composition string.
  654. * Out param in szCompStr.
  655. */
  656. INT CIme::GetCompositionStringInfo(
  657. HIMC hIMC, // @parm IME context provided by host.
  658. DWORD dwIndex, // @parm The type of composition string.
  659. WCHAR *szCompStr, // @parm Out param, unicode result string.
  660. INT cchMax, // @parm The cch for the Out param.
  661. BYTE *attrib, // @parm Out param, If attribute info is needed.
  662. INT cbMax, // @parm The cb of the attribute info.
  663. LONG *cpCursor, // @parm Out param, returns the CP of cusor.
  664. LONG *cchAttrib, // @parm how many attributes returned.
  665. UINT kbCodePage, // @parm codepage
  666. BOOL bUnicodeIME, // @parm Unciode IME
  667. BOOL bUsingAimm) // @parm Using Aimm
  668. {
  669. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::GetCompositionStringInfo");
  670. BYTE compStr[256], attribInfo[256];
  671. INT i, j, iMax, cchCompStr=0, cbAttrib, cursor;
  672. INT cchAnsiCompStr=0;
  673. Assert(hIMC && szCompStr);
  674. if(cpCursor) // Init cursor out param.
  675. *cpCursor = -1;
  676. if(cchAttrib)
  677. *cchAttrib = 0;
  678. // Get composition string.
  679. if (bUnicodeIME)
  680. cchCompStr = ImmGetCompositionStringW(hIMC, dwIndex, szCompStr, cchMax, bUsingAimm )/sizeof(WCHAR);
  681. else
  682. cchAnsiCompStr = ImmGetCompositionStringA(hIMC, dwIndex, compStr, 255, bUsingAimm);
  683. if(cchAnsiCompStr > 0 || cchCompStr > 0) // If valid data.
  684. {
  685. if (!bUnicodeIME)
  686. {
  687. Assert(cchAnsiCompStr >> 1 < cchMax - 1); // Convert to Unicode.
  688. cchCompStr = UnicodeFromMbcs(szCompStr, cchMax,
  689. (CHAR *) compStr, cchAnsiCompStr, kbCodePage);
  690. }
  691. if(attrib || cpCursor) // Need cursor or attribs?
  692. {
  693. if (bUnicodeIME)
  694. { // Get Unicode Cursor cp.
  695. cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0, bUsingAimm);
  696. // Get Unicode attributes.
  697. cbAttrib = ImmGetCompositionStringW(hIMC, GCS_COMPATTR,
  698. attribInfo, 255, bUsingAimm);
  699. iMax = max(cursor, cbAttrib);
  700. iMax = min(iMax, cchCompStr);
  701. }
  702. else
  703. { // Get DBCS Cursor cp.
  704. cursor = ImmGetCompositionStringA(hIMC, GCS_CURSORPOS, NULL, 0, bUsingAimm);
  705. // Get DBCS attributes.
  706. cbAttrib = ImmGetCompositionStringA(hIMC, GCS_COMPATTR,
  707. attribInfo, 255, bUsingAimm);
  708. iMax = max(cursor, cbAttrib);
  709. iMax = min(iMax, cchAnsiCompStr);
  710. }
  711. if(NULL == attrib)
  712. cbMax = cbAttrib;
  713. for(i = 0, j = 0; i <= iMax && j < cbMax; i++, j++)
  714. {
  715. if(cursor == i)
  716. cursor = j;
  717. if(!bUnicodeIME && GetTrailBytesCount(compStr[i], kbCodePage))
  718. i++;
  719. if(attrib && i < cbAttrib)
  720. *attrib++ = attribInfo[i];
  721. }
  722. // attrib cch==unicode cch
  723. Assert(0 >= cbAttrib || j-1 == cchCompStr);
  724. if(cursor >= 0 && cpCursor) // If client needs cursor
  725. *cpCursor = cursor; // or cchAttrib.
  726. if(cbAttrib >= 0 && cchAttrib)
  727. *cchAttrib = j-1;
  728. }
  729. }
  730. else
  731. {
  732. if(cpCursor)
  733. *cpCursor = 0;
  734. cchCompStr = 0;
  735. }
  736. return cchCompStr;
  737. }
  738. /*
  739. * void CIme::SetCompositionFont (CTextMsgFilter &TextMsgFilter, ITextFont *pTextFont)
  740. *
  741. * @mfunc
  742. * Important for level 2 IME so that the composition window
  743. * has the correct font. The lfw to lfa copy is due to the fact that
  744. * Win95 does not support the W)ide call.
  745. * It is also important for both level 2 and level 3 IME so that
  746. * the candidate list window has the proper. font.
  747. */
  748. void CIme::SetCompositionFont (
  749. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  750. ITextFont *pTextFont) // @parm ITextFont for setting lfa.
  751. {
  752. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::SetCompositionFont");
  753. HIMC hIMC;
  754. LOGFONTA lfa;
  755. if (pTextFont)
  756. {
  757. hIMC = LocalGetImmContext(TextMsgFilter);
  758. if (hIMC)
  759. {
  760. // Build the LOGFONT based on pTextFont
  761. float FontSize;
  762. long lValue;
  763. BSTR bstr;
  764. long lTextFlow;
  765. memset (&lfa, 0, sizeof(lfa));
  766. if (pTextFont->GetSize(&FontSize) == NOERROR)
  767. lfa.lfHeight = (LONG) FontSize;
  768. if (pTextFont->GetBold(&lValue) == NOERROR && lValue == tomTrue)
  769. lfa.lfWeight = FW_BOLD;
  770. if (pTextFont->GetItalic(&lValue) == NOERROR && lValue == tomTrue)
  771. lfa.lfItalic = TRUE;
  772. lfa.lfCharSet = (BYTE)GetCharSet(TextMsgFilter._uKeyBoardCodePage);
  773. lValue = tomCharset;
  774. if (pTextFont->GetLanguageID(&lValue) == NOERROR &&
  775. lfa.lfCharSet == (BYTE)lValue)
  776. lfa.lfPitchAndFamily = (BYTE)(lValue >> 8);
  777. if (pTextFont->GetName(&bstr) == NOERROR)
  778. {
  779. if ((TextMsgFilter._lFEFlags & tomUseAtFont) && bstr[0] != L'@')
  780. {
  781. lfa.lfFaceName[0] = '@';
  782. MbcsFromUnicode(&(lfa.lfFaceName[1]), sizeof(lfa.lfFaceName)-1, bstr,
  783. -1, CP_ACP, UN_NOOBJECTS);
  784. }
  785. else
  786. MbcsFromUnicode(lfa.lfFaceName, sizeof(lfa.lfFaceName), bstr,
  787. -1, CP_ACP, UN_NOOBJECTS);
  788. SysFreeString(bstr);
  789. }
  790. lTextFlow = TextMsgFilter._lFEFlags & tomTextFlowMask;
  791. if (lTextFlow)
  792. {
  793. DWORD imeUIProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_UI, TextMsgFilter._fUsingAIMM);
  794. if (imeUIProperties & (UI_CAP_2700 | UI_CAP_ROT90 | UI_CAP_ROTANY))
  795. {
  796. if (lTextFlow == tomTextFlowSW)
  797. lfa.lfOrientation = lfa.lfEscapement = 2700;
  798. else if (lTextFlow == tomTextFlowNE)
  799. lfa.lfOrientation = lfa.lfEscapement = 900;
  800. }
  801. }
  802. ImmSetCompositionFontA( hIMC, &lfa, TextMsgFilter._fUsingAIMM );
  803. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  804. }
  805. }
  806. }
  807. /*
  808. * void CIme::SetCompositionForm (CTextMsgFilter &TextMsgFilter)
  809. *
  810. * @mfunc
  811. * Important for level 2 IME so that the composition window
  812. * is positioned correctly.
  813. *
  814. * @comm
  815. * We go through a lot of work to get the correct height. This requires
  816. * getting information from the font cache and the selection.
  817. */
  818. void CIme::SetCompositionForm (
  819. CTextMsgFilter &TextMsgFilter) // @parm the containing text edit.
  820. {
  821. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::SetCompositionForm");
  822. HIMC hIMC;
  823. COMPOSITIONFORM cf;
  824. if(IME_LEVEL_2 == GetIMELevel())
  825. {
  826. hIMC = LocalGetImmContext(TextMsgFilter); // Get IME context.
  827. if(hIMC)
  828. {
  829. // get the location of cpMin
  830. cf.ptCurrentPos.x = cf.ptCurrentPos.y = 0;
  831. TextMsgFilter._pTextSel->GetPoint( tomStart+tomClientCoord+TA_BOTTOM+TA_LEFT,
  832. &(cf.ptCurrentPos.x), &(cf.ptCurrentPos.y) );
  833. // Set-up bounding rect. for the IME (lev 2) composition window, causing
  834. // composition text to be wrapped within it.
  835. cf.dwStyle = CFS_RECT;
  836. TextMsgFilter._pTextDoc->GetClientRect(tomIncludeInset+tomClientCoord,
  837. &(cf.rcArea.left), &(cf.rcArea.top),
  838. &(cf.rcArea.right), &(cf.rcArea.bottom));
  839. // Make sure the starting point is not
  840. // outside the rcArea. This happens when
  841. // there is no text on the current line and the user
  842. // has selected a large font size.
  843. if(cf.ptCurrentPos.y < cf.rcArea.top)
  844. cf.ptCurrentPos.y = cf.rcArea.top;
  845. else if(cf.ptCurrentPos.y > cf.rcArea.bottom)
  846. cf.ptCurrentPos.y = cf.rcArea.bottom;
  847. if(cf.ptCurrentPos.x < cf.rcArea.left)
  848. cf.ptCurrentPos.x = cf.rcArea.left;
  849. else if(cf.ptCurrentPos.x > cf.rcArea.right)
  850. cf.ptCurrentPos.x = cf.rcArea.right;
  851. ImmSetCompositionWindow(hIMC, &cf, TextMsgFilter._fUsingAIMM); // Set composition window.
  852. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  853. }
  854. }
  855. }
  856. /*
  857. *
  858. * CIme::TerminateIMEComposition (CTextMsgFilter &TextMsgFilter)
  859. *
  860. * @mfunc Terminate the IME Composition mode using CPS_COMPLETE
  861. * @comm The IME will generate WM_IME_COMPOSITION with the result string
  862. *
  863. */
  864. void CIme::TerminateIMEComposition(
  865. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  866. TerminateMode mode)
  867. {
  868. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::TerminateIMEComposition");
  869. DWORD dwTerminateMethod;
  870. HIMC hIMC = LocalGetImmContext(TextMsgFilter);
  871. if(TextMsgFilter.IsIMEComposition() && TextMsgFilter._ime->IsTerminated())
  872. {
  873. // Turn if off now
  874. EndCompositionGlue(TextMsgFilter, TRUE);
  875. return;
  876. }
  877. _fIMETerminated = TRUE;
  878. if (mode == TERMINATE_FORCECANCEL)
  879. TextMsgFilter._pTextDoc->IMEInProgress(tomFalse); // Inform client IME compostion is done
  880. dwTerminateMethod = CPS_COMPLETE;
  881. if (IME_LEVEL_2 == GetIMELevel() || // force cancel for near-caret IME
  882. mode == TERMINATE_FORCECANCEL || // caller wants force cancel
  883. TextMsgFilter._fIMECancelComplete) // Client wants force cancel
  884. {
  885. dwTerminateMethod = CPS_CANCEL;
  886. }
  887. // force the IME to terminate the current session
  888. if(hIMC)
  889. {
  890. BOOL fRetCode;
  891. fRetCode = ImmNotifyIME(hIMC, NI_COMPOSITIONSTR,
  892. dwTerminateMethod, 0, TextMsgFilter._fUsingAIMM);
  893. if(!fRetCode && !TextMsgFilter._fIMECancelComplete)
  894. {
  895. // CPS_COMPLETE fail, try CPS_CANCEL. This happen with some ime which do not support
  896. // CPS_COMPLETE option (e.g. ABC IME version 4 with Win95 simplified Chinese)
  897. fRetCode = ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0, TextMsgFilter._fUsingAIMM);
  898. }
  899. LocalReleaseImmContext(TextMsgFilter, hIMC);
  900. }
  901. else
  902. {
  903. // for some reason, we didn't have a context, yet we thought we were still in IME
  904. // compostition mode. Just force a shutdown here.
  905. EndCompositionGlue(TextMsgFilter, TRUE);
  906. }
  907. }
  908. /*
  909. * CIme_Lev2::CIme_Lev2()
  910. *
  911. * @mfunc
  912. * CIme_Lev2 Constructor/Destructor.
  913. *
  914. * @comm
  915. * Needed to make sure _iFormatSave was handled properly.
  916. *
  917. */
  918. CIme_Lev2::CIme_Lev2(
  919. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  920. {
  921. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::CIme_Lev2");
  922. long cpMin, cpMax, cpLoc;
  923. HRESULT hResult;
  924. ITextFont *pCurrentFont = NULL;
  925. _pTextFont = NULL;
  926. _cIgnoreIMECharMsg = 0;
  927. // setup base Font format for later use during composition
  928. hResult = TextMsgFilter._pTextSel->GetStart(&cpMin);
  929. cpLoc = cpMin;
  930. if (TextMsgFilter._fHangulToHanja)
  931. cpMax = cpMin + 1; // Select the Hangul character
  932. else
  933. hResult = TextMsgFilter._pTextSel->GetEnd(&cpMax);
  934. _fSkipFirstOvertype = FALSE;
  935. if (cpMax != cpMin)
  936. {
  937. // selection case, get format for at cpMin
  938. ITextRange *pTextRange;
  939. HRESULT hResult;
  940. hResult = TextMsgFilter._pTextDoc->Range(cpMin, cpMin+1, &pTextRange);
  941. Assert (pTextRange != NULL);
  942. if (hResult == NOERROR && pTextRange)
  943. {
  944. pTextRange->GetFont(&pCurrentFont);
  945. Assert(pCurrentFont != NULL);
  946. pTextRange->Release();
  947. cpLoc = cpMin+1;
  948. }
  949. if (!TextMsgFilter._fHangulToHanja)
  950. _fSkipFirstOvertype = TRUE; // For Korean Overtype support
  951. }
  952. if (!pCurrentFont)
  953. TextMsgFilter._pTextSel->GetFont(&pCurrentFont);
  954. Assert(pCurrentFont != NULL);
  955. pCurrentFont->GetDuplicate(&_pTextFont); // duplicate the base format for later use
  956. pCurrentFont->Release();
  957. Assert(_pTextFont != NULL);
  958. // setup font to match current keyboard
  959. CIme::CheckKeyboardFontMatching (cpLoc, &TextMsgFilter, _pTextFont);
  960. _fIgnoreEndComposition = FALSE;
  961. _fIMETerminated = FALSE;
  962. }
  963. CIme_Lev2::~CIme_Lev2()
  964. {
  965. if ( _pTextFont )
  966. _pTextFont->Release();
  967. }
  968. /*
  969. * HRESULT CIme_Lev2::StartComposition(CTextMsgFilter &TextMsgFilter)
  970. *
  971. * @mfunc
  972. * Begin IME Level 2 composition string processing.
  973. *
  974. * @comm
  975. * Set the font, and location of the composition window which includes
  976. * a bounding rect and the start position of the cursor. Also, reset
  977. * the candidate window to allow the IME to set its position.
  978. *
  979. * @rdesc
  980. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  981. */
  982. HRESULT CIme_Lev2::StartComposition(
  983. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  984. {
  985. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::StartComposition");
  986. _imeLevel = IME_LEVEL_2;
  987. SetCompositionFont(TextMsgFilter, _pTextFont); // Set font, & comp window.
  988. SetCompositionForm(TextMsgFilter);
  989. return S_FALSE; // Allow DefWindowProc
  990. } // processing.
  991. /*
  992. * HRESULT CIme_Lev2::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  993. *
  994. * @mfunc
  995. * Handle Level 2 WM_IME_COMPOSITION messages.
  996. *
  997. * @rdesc
  998. * HRESULT-S_FALSE for DefWindowProc processing.
  999. *
  1000. * Side effect:
  1001. * The Host needs to mask out the lparam before calling DefWindowProc to
  1002. * prevent unnessary WM_IME_CHAR messages.
  1003. */
  1004. HRESULT CIme_Lev2::CompositionString (
  1005. const LPARAM lparam, // @parm associated with message.
  1006. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1007. {
  1008. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::CompositionString");
  1009. _cIgnoreIMECharMsg = 0;
  1010. if(HAVE_RESULT_STRING())
  1011. {
  1012. _fGotFinalString = TRUE;
  1013. if (_pTextFont)
  1014. {
  1015. // setup the font before insert final string
  1016. ITextFont *pFETextFont=NULL;
  1017. _pTextFont->GetDuplicate(&pFETextFont);
  1018. Assert(pFETextFont != NULL);
  1019. TextMsgFilter._pTextSel->SetFont(pFETextFont);
  1020. pFETextFont->Release();
  1021. }
  1022. TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue);
  1023. CheckInsertResultString(lparam, TextMsgFilter, &_cIgnoreIMECharMsg);
  1024. SetCompositionForm(TextMsgFilter); // Move Composition window.
  1025. }
  1026. // Always return S_FALSE so the DefWindowProc will handle the rest.
  1027. // Host has to mask out the ResultString bit to avoid WM_IME_CHAR coming in.
  1028. return S_FALSE;
  1029. }
  1030. /*
  1031. * HRESULT CIme::CheckInsertResultString (const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  1032. *
  1033. * @mfunc
  1034. * handle inserting of GCS_RESULTSTR text, the final composed text.
  1035. *
  1036. * @comm
  1037. * When the final composition string arrives we grab it and set it into the text.
  1038. *
  1039. * @devnote
  1040. * A GCS_RESULTSTR message can arrive and the IME will *still* be in
  1041. * composition string mode. This occurs because the IME's internal
  1042. * buffers overflowed and it needs to convert the beginning of the buffer
  1043. * to clear out some room. When this happens we need to insert the
  1044. * converted text as normal, but remain in composition processing mode.
  1045. *
  1046. * @rdesc
  1047. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  1048. */
  1049. HRESULT CIme::CheckInsertResultString (
  1050. const LPARAM lparam, // @parm associated with message.
  1051. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  1052. short *pcch, // @parm number of character read
  1053. int *pcbOutBuff, // @parm byte size of the output buffer
  1054. WCHAR *pOutBuff) // @parm buffer to receive the text
  1055. {
  1056. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme::CheckInsertResultString");
  1057. HRESULT hr = S_FALSE;
  1058. HIMC hIMC;
  1059. INT cch;
  1060. WCHAR szCompStr[256];
  1061. if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // If result string..
  1062. {
  1063. hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1064. cch = 0;
  1065. if(hIMC) // Get result string.
  1066. {
  1067. cch = GetCompositionStringInfo(hIMC, pOutBuff ? GCS_RESULTREADSTR : GCS_RESULTSTR,
  1068. szCompStr,
  1069. sizeof(szCompStr)/sizeof(szCompStr[0]),
  1070. NULL, 0, NULL, NULL, TextMsgFilter._uKeyBoardCodePage,
  1071. TextMsgFilter._fUnicodeIME, TextMsgFilter._fUsingAIMM);
  1072. if (pcch)
  1073. *pcch = (short)cch;
  1074. cch = min (cch, 255);
  1075. szCompStr[cch] = L'\0';
  1076. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  1077. }
  1078. // Don't need to replace range when there isn't any text. Otherwise, the character format is
  1079. // reset to previous run.
  1080. if(cch)
  1081. {
  1082. if (pOutBuff)
  1083. {
  1084. if (*pcbOutBuff > (int) ((cch + 1) * sizeof(WCHAR)))
  1085. *pcbOutBuff = (cch + 1) * sizeof(WCHAR);
  1086. memcpy(pOutBuff, szCompStr, *pcbOutBuff);
  1087. }
  1088. else
  1089. {
  1090. BSTR bstr = SysAllocString(szCompStr);
  1091. if (!bstr)
  1092. return E_OUTOFMEMORY;
  1093. TextMsgFilter._pTextSel->TypeText(bstr);
  1094. SysFreeString(bstr);
  1095. }
  1096. }
  1097. else if (pOutBuff)
  1098. *pcbOutBuff = 0;
  1099. hr = S_OK; // Don't want WM_IME_CHARs.
  1100. }
  1101. return hr;
  1102. }
  1103. /*
  1104. * HRESULT CIme_Lev2::IMENotify(const WPARAM wparam, const LPARAM lparam,
  1105. * CTextMsgFilter &TextMsgFilter)
  1106. *
  1107. * @mfunc
  1108. * Handle Level 2 WM_IME_NOTIFY messages.
  1109. *
  1110. * @comm
  1111. * Currently we are only interested in knowing when to reset
  1112. * the candidate window's position.
  1113. *
  1114. * @rdesc
  1115. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  1116. */
  1117. HRESULT CIme_Lev2::IMENotify(
  1118. const WPARAM wparam, // @parm associated with message.
  1119. const LPARAM lparam, // @parm associated with message.
  1120. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  1121. BOOL fIgnore) // @parm Level3 Chinese Composition window only
  1122. {
  1123. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::IMENotify");
  1124. if(IMN_OPENCANDIDATE == wparam)
  1125. {
  1126. Assert (0 != lparam);
  1127. HIMC hIMC; // Host's IME context.
  1128. INT index; // Candidate window index.
  1129. CANDIDATEFORM cdCandForm;
  1130. hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1131. if(hIMC)
  1132. {
  1133. // Convert bitID to INDEX.
  1134. for (index = 0; index < 32; index++) // because *stupid* API.
  1135. {
  1136. if((1 << index) & lparam)
  1137. break;
  1138. }
  1139. Assert (((1 << index) & lparam) == lparam); // Only 1 set?
  1140. Assert (index < 32);
  1141. // Reset to CFS_DEFAULT
  1142. if(ImmGetCandidateWindow(hIMC, index, &cdCandForm, TextMsgFilter._fUsingAIMM)
  1143. && CFS_DEFAULT != cdCandForm.dwStyle)
  1144. {
  1145. cdCandForm.dwStyle = CFS_DEFAULT;
  1146. ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM);
  1147. }
  1148. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  1149. }
  1150. }
  1151. return S_FALSE; // Allow DefWindowProc
  1152. } // processing.
  1153. /*
  1154. * void CIme_Lev2::PostIMEChar (CTextMsgFilter &TextMsgFilter)
  1155. *
  1156. * @mfunc
  1157. * Called after processing a single WM_IME_CHAR in order to
  1158. * update the position of the IME's composition window.
  1159. *
  1160. */
  1161. void CIme_Lev2::PostIMEChar (
  1162. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1163. {
  1164. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::PostIMEChar");
  1165. SetCompositionForm(TextMsgFilter); // Move Composition window.
  1166. }
  1167. /*
  1168. *
  1169. * CIme_Lev2::IMEMouseOperation (CTextMsgFilter &TextMsgFilter, UINT msg, BOOL &fTerminateIME)
  1170. *
  1171. * @mfunc Level 2 IME does not handle the mouse events, we need to check if
  1172. * we should terminate IME.
  1173. *
  1174. * @rdesc
  1175. * BOOL-FALSE since Level 2 IME does not handle the mouse events
  1176. * fTermineateIME-TRUE if we want to terminateIME
  1177. *
  1178. */
  1179. BOOL CIme_Lev2::IMEMouseOperation(
  1180. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  1181. UINT msg, // @parm message id
  1182. WPARAM wParam, // @parm wparam
  1183. BOOL &fTerminateIME) // @parm need to terminate IME
  1184. {
  1185. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::IMEMouseOperation");
  1186. // Level 2 IME, check if we need to terminate IME
  1187. fTerminateIME = FALSE;
  1188. switch(msg)
  1189. {
  1190. case WM_LBUTTONDOWN:
  1191. case WM_LBUTTONDBLCLK:
  1192. case WM_RBUTTONDOWN:
  1193. case WM_RBUTTONDBLCLK:
  1194. case WM_MBUTTONDOWN:
  1195. case WM_MBUTTONDBLCLK:
  1196. fTerminateIME = TRUE;
  1197. }
  1198. return FALSE;
  1199. }
  1200. /*
  1201. *
  1202. * CIme_Lev2::GetIMECompositionMode (CTextMsgFilter &TextMsgFilter)
  1203. *
  1204. * @mfunc Return the current IME composition mode when we haven't received any final string
  1205. *
  1206. * @rdesc
  1207. * IME Level
  1208. *
  1209. */
  1210. LRESULT CIme_Lev2::GetIMECompositionMode(
  1211. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1212. {
  1213. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev2::GetIMECompositionMode");
  1214. LRESULT lres = ICM_NOTOPEN;
  1215. if (!_fGotFinalString)
  1216. {
  1217. DWORD imeProperties;
  1218. imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_PROPERTY, TextMsgFilter._fUsingAIMM);
  1219. if(imeProperties & IME_PROP_AT_CARET)
  1220. lres = ICM_LEVEL2_5; // level 2.5.
  1221. else if (imeProperties & IME_PROP_SPECIAL_UI)
  1222. lres = ICM_LEVEL2_SUI; // special UI.
  1223. else
  1224. lres = ICM_LEVEL2; // stock level 2.
  1225. }
  1226. return lres;
  1227. }
  1228. /*
  1229. * CIme_Lev3::CIme_Lev3()
  1230. *
  1231. * @mfunc
  1232. * CIme_Lev3 Constructor/Destructor.
  1233. *
  1234. */
  1235. CIme_Lev3::CIme_Lev3(
  1236. CTextMsgFilter &TextMsgFilter) : CIme_Lev2 ( TextMsgFilter )
  1237. {
  1238. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::CIme_Lev3");
  1239. _sIMESuportMouse = 0; // initial to 0 so we will check mouse operation if need
  1240. _wParamBefore = 0;
  1241. _fUpdateWindow = FALSE;
  1242. }
  1243. /*
  1244. * HRESULT CIme_Lev3::StartComposition(CTextMsgFilter &TextMsgFilter)
  1245. *
  1246. * @mfunc
  1247. * Begin IME Level 3 composition string processing.
  1248. *
  1249. * @comm
  1250. * For rudimentary processing, remember the start and
  1251. * length of the selection. Set the font in case the
  1252. * candidate window actually uses this information.
  1253. *
  1254. * @rdesc
  1255. * This is a rudimentary solution for remembering were
  1256. * the composition is in the text. There needs to be work
  1257. * to replace this with a composition "range".
  1258. *
  1259. * @rdesc
  1260. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  1261. */
  1262. HRESULT CIme_Lev3::StartComposition(
  1263. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1264. {
  1265. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::StartComposition");
  1266. long cpMin;
  1267. TextMsgFilter._pTextSel->GetStart(&cpMin);
  1268. _ichStart = cpMin;
  1269. _cchCompStr = 0;
  1270. _imeLevel = IME_LEVEL_3;
  1271. SetCompositionFont (TextMsgFilter, _pTextFont);
  1272. // Delete current selection
  1273. TextMsgFilter._pTextSel->SetText(NULL);
  1274. // turn off undo
  1275. TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL);
  1276. if (_pTextFont)
  1277. {
  1278. _pTextFont->GetForeColor(&_crTextColor);
  1279. _pTextFont->GetBackColor(&_crBkColor);
  1280. }
  1281. // Setup IMEShare Lid if necessary
  1282. if (!TextMsgFilter._fRE10Mode &&
  1283. TextMsgFilter._uKeyBoardCodePage != CP_KOREAN &&
  1284. W32->HaveIMEShare())
  1285. {
  1286. CIMEShare *pIMEShare;
  1287. if (W32->getIMEShareObject(&pIMEShare))
  1288. {
  1289. LID hKL = (LID)GetKeyboardLayout(0x0FFFFFFFF);
  1290. if (pIMEShare->LidGetLid() != hKL)
  1291. pIMEShare->LidSetLid(hKL);
  1292. }
  1293. }
  1294. return S_OK; // No DefWindowProc
  1295. } // processing.
  1296. /*
  1297. * HRESULT CIme_Lev3::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  1298. *
  1299. * @mfunc
  1300. * Handle Level 3 WM_IME_COMPOSITION messages.
  1301. *
  1302. * @comm
  1303. * Display all of the intermediary composition text as well as the final
  1304. * reading.
  1305. *
  1306. * @devnote
  1307. * This is a rudimentary solution for replacing text in the backing store.
  1308. * Work is left to do with the undo list, underlining, and hiliting with
  1309. * colors and the selection.
  1310. *
  1311. * @devnote
  1312. * A GCS_RESULTSTR message can arrive and the IME will *still* be in
  1313. * composition string mode. This occurs because the IME's internal
  1314. * buffers overflowed and it needs to convert the beginning of the buffer
  1315. * to clear out some room. When this happens we need to insert the
  1316. * converted text as normal, but remain in composition processing mode.
  1317. *
  1318. * Another reason, GCS_RESULTSTR can occur while in composition mode
  1319. * for Korean because there is only 1 correct choice and no additional
  1320. * user intervention is necessary, meaning that the converted string can
  1321. * be sent as the result before composition mode is finished.
  1322. *
  1323. * @rdesc
  1324. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  1325. */
  1326. HRESULT CIme_Lev3::CompositionString(
  1327. const LPARAM lparam, // @parm associated with message.
  1328. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1329. {
  1330. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::CompositionString");
  1331. long cpMin;
  1332. _fIgnoreEndComposition = FALSE;
  1333. if (_fUpdateWindow)
  1334. {
  1335. TextMsgFilter._pTextDoc->UpdateWindow();
  1336. _fUpdateWindow = FALSE;
  1337. }
  1338. if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // Any final readings?
  1339. {
  1340. long lCount;
  1341. if (!_fHandlingFinalString)
  1342. {
  1343. _fHandlingFinalString = TRUE;
  1344. if (HAVE_RESULT_STRING())
  1345. _fGotFinalString = TRUE;
  1346. if (!CLEANUP_COMPOSITION_STRING())
  1347. TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display
  1348. if (_cchCompStr)
  1349. {
  1350. ITextRange *pTextRange = NULL;
  1351. // Create a range to delete composition text
  1352. TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart + _cchCompStr, &pTextRange);
  1353. Assert (pTextRange != NULL);
  1354. // delete composition text
  1355. pTextRange->SetText(NULL);
  1356. pTextRange->Release();
  1357. _cchCompStr = 0; // be in composition mode.
  1358. };
  1359. // setup the font before insert final string
  1360. ITextFont *pFETextFont;
  1361. _pTextFont->GetDuplicate(&pFETextFont);
  1362. Assert(pFETextFont != NULL);
  1363. TextMsgFilter._pTextSel->SetFont(pFETextFont);
  1364. pFETextFont->Release();
  1365. // turn on undo
  1366. TextMsgFilter._pTextDoc->Undo(tomResume, NULL);
  1367. // Turn on Notification again
  1368. TextMsgFilter._pTextDoc->SetNotificationMode(tomTrue);
  1369. // get final string
  1370. CheckInsertResultString(lparam, TextMsgFilter);
  1371. if (!CLEANUP_COMPOSITION_STRING())
  1372. TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display
  1373. // Reset as we may still in Composition
  1374. TextMsgFilter._pTextSel->GetStart(&cpMin);
  1375. _ichStart = cpMin;
  1376. // turn off undo for Korean IME since we will get Composition string message
  1377. // again without getting EndComposition
  1378. if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)
  1379. TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL);
  1380. _fHandlingFinalString = FALSE;
  1381. }
  1382. }
  1383. if(HAVE_COMPOSITION_STRING()) // In composition mode?
  1384. {
  1385. HIMC hIMC;
  1386. INT cchOld = _cchCompStr;
  1387. LONG cpCursor = 0, cchAttrib = 0;
  1388. LONG i, j; // For applying attrib effects.
  1389. WCHAR szCompStr[256];
  1390. BYTE startAttrib, attrib[256];
  1391. BSTR bstr = NULL;
  1392. ITextRange *pTextRange = NULL;
  1393. long cpMax;
  1394. long lCount;
  1395. _cchCompStr = 0;
  1396. if (!_fDestroy)
  1397. {
  1398. hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1399. if(hIMC) // Get composition string.
  1400. {
  1401. _cchCompStr = GetCompositionStringInfo(hIMC, GCS_COMPSTR,
  1402. szCompStr, sizeof(szCompStr)/sizeof(szCompStr[0]),
  1403. attrib, sizeof(attrib)/sizeof(attrib[0]),
  1404. &cpCursor, &cchAttrib, TextMsgFilter._uKeyBoardCodePage, TextMsgFilter._fUnicodeIME, TextMsgFilter._fUsingAIMM);
  1405. _cchCompStr = min (_cchCompStr, 255);
  1406. szCompStr[_cchCompStr] = L'\0';
  1407. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  1408. }
  1409. }
  1410. // any new composition string?
  1411. if(_cchCompStr)
  1412. {
  1413. long cchExced = 0;
  1414. if (TextMsgFilter._pTextDoc->CheckTextLimit(_cchCompStr-cchOld, &cchExced) == NOERROR &&
  1415. cchExced > 0)
  1416. {
  1417. // We reach text limit, beep...
  1418. TextMsgFilter._pTextDoc->SysBeep();
  1419. if (_cchCompStr > cchExced)
  1420. _cchCompStr -= cchExced;
  1421. else
  1422. _cchCompStr = 0;
  1423. szCompStr[_cchCompStr] = L'\0';
  1424. if (!_cchCompStr && TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)
  1425. TextMsgFilter._pTextDoc->SetCaretType(tomNormalCaret); // Turn off Block caret mode
  1426. }
  1427. bstr = SysAllocString(szCompStr);
  1428. if (!bstr)
  1429. return E_OUTOFMEMORY;
  1430. if (HAVE_RESULT_STRING())
  1431. {
  1432. // ignore next end composition
  1433. _fIgnoreEndComposition = TRUE;
  1434. // turn off undo
  1435. TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL);
  1436. // Get the new format that may have changed by apps (e.g. Outlook)
  1437. _pTextFont->Release();
  1438. ITextFont *pCurrentFont = NULL;
  1439. TextMsgFilter._pTextSel->GetFont(&pCurrentFont);
  1440. Assert(pCurrentFont != NULL);
  1441. pCurrentFont->GetDuplicate(&_pTextFont); // duplicate the base format for later use
  1442. pCurrentFont->Release();
  1443. Assert(_pTextFont != NULL);
  1444. CIme::CheckKeyboardFontMatching (_ichStart, &TextMsgFilter, _pTextFont);
  1445. }
  1446. }
  1447. if (cchOld || _cchCompStr)
  1448. {
  1449. bool fFreezeDisplay = false;
  1450. // Hold notification if needed
  1451. if (!(TextMsgFilter._fIMEAlwaysNotify) && !HAVE_RESULT_STRING())
  1452. TextMsgFilter._pTextDoc->SetNotificationMode(tomFalse);
  1453. // We only support overtype mode in Korean IME
  1454. if (!cchOld && TextMsgFilter._uKeyBoardCodePage == CP_KOREAN &&
  1455. TextMsgFilter._fOvertypeMode && !_fSkipFirstOvertype)
  1456. {
  1457. long cCurrentChar;
  1458. HRESULT hResult;
  1459. // Create a range using the next character
  1460. hResult = TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart+1, &pTextRange);
  1461. Assert (pTextRange != NULL);
  1462. // Check if it is par character. If so, we don't want to
  1463. // delete it.
  1464. hResult = pTextRange->GetChar(&cCurrentChar);
  1465. if (hResult == NOERROR)
  1466. {
  1467. if (cCurrentChar != (long)'\r' && cCurrentChar != (long)'\n')
  1468. {
  1469. TextMsgFilter._pTextDoc->Undo(tomResume, NULL); // Turn on undo
  1470. pTextRange->SetText(NULL); // Delete the character
  1471. TextMsgFilter._pTextDoc->Undo(tomSuspend, NULL); // Turn off undo
  1472. }
  1473. else
  1474. {
  1475. // Unselect the par character
  1476. hResult = pTextRange->SetRange(_ichStart, _ichStart);
  1477. }
  1478. }
  1479. }
  1480. else
  1481. {
  1482. // Create a range using the preivous composition text and delete the text
  1483. TextMsgFilter._pTextDoc->Range(_ichStart, _ichStart+cchOld, &pTextRange);
  1484. Assert (pTextRange != NULL);
  1485. if (cchOld)
  1486. {
  1487. if (cpCursor >= 0)
  1488. {
  1489. TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display
  1490. fFreezeDisplay = true;
  1491. }
  1492. pTextRange->SetText(NULL);
  1493. }
  1494. }
  1495. _fSkipFirstOvertype = FALSE;
  1496. if (cpCursor >= 0 && !fFreezeDisplay)
  1497. TextMsgFilter._pTextDoc->Freeze(&lCount); // Turn off display
  1498. // Make sure the composition string is formatted with the base font
  1499. ITextFont *pFETextFont;
  1500. HRESULT hResult;
  1501. hResult = _pTextFont->GetDuplicate(&pFETextFont);
  1502. Assert(pFETextFont != NULL);
  1503. if (!(hResult != NOERROR || pFETextFont == NULL))
  1504. {
  1505. if (TextMsgFilter._fHangulToHanja && !_cchCompStr)
  1506. // Hangul to Hanja mode, setup font for selection to
  1507. // handle the Hanja character the come in after the end composition
  1508. // message
  1509. TextMsgFilter._pTextSel->SetFont(pFETextFont);
  1510. else
  1511. pTextRange->SetFont(pFETextFont);
  1512. }
  1513. pTextRange->SetText(bstr); // Replace with the new text
  1514. if (pFETextFont)
  1515. pFETextFont->Release();
  1516. // update how many composition characters have been added
  1517. pTextRange->GetEnd(&cpMax);
  1518. _cchCompStr = cpMax - _ichStart;
  1519. if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)
  1520. {
  1521. // no formatting for Korean
  1522. POINT ptBottomPos;
  1523. if (cpCursor == 0)
  1524. TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display
  1525. if (pTextRange->GetPoint( tomEnd+TA_BOTTOM+TA_RIGHT,
  1526. &(ptBottomPos.x), &(ptBottomPos.y) ) != NOERROR)
  1527. pTextRange->ScrollIntoView(tomEnd);
  1528. // Setup Block caret mode only when there is One char and the caret pos is 0.
  1529. TextMsgFilter._pTextDoc->SetCaretType((_cchCompStr == 1 && !cpCursor) ? tomKoreanBlockCaret : tomNormalCaret);
  1530. }
  1531. else if (_cchCompStr && _cchCompStr <= cchAttrib)
  1532. {
  1533. for ( i = 0; i < _cchCompStr; ) // Parse the attributes...
  1534. { // to apply styles.
  1535. ITextFont *pFETextFont;
  1536. HRESULT hResult;
  1537. hResult = _pTextFont->GetDuplicate(&pFETextFont);
  1538. Assert(pFETextFont != NULL);
  1539. if (hResult != NOERROR || pFETextFont == NULL)
  1540. break;
  1541. // Rsest the clone font so we will only apply effects returned
  1542. // from SetCompositionStyle
  1543. pFETextFont->Reset(tomUndefined);
  1544. startAttrib = attrib[i]; // Get attrib's run length.
  1545. for ( j = i+1; j < _cchCompStr; j++ )
  1546. {
  1547. if ( startAttrib != attrib[j] ) // Same run until diff.
  1548. break;
  1549. }
  1550. SetCompositionStyle(TextMsgFilter, startAttrib, pFETextFont);
  1551. // Apply FE clause's style
  1552. pTextRange->SetRange(_ichStart+i, _ichStart+j);
  1553. pTextRange->SetFont(pFETextFont);
  1554. pFETextFont->Release();
  1555. i = j;
  1556. }
  1557. }
  1558. pTextRange->Release();
  1559. }
  1560. else if (TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)
  1561. TextMsgFilter._pTextDoc->Update(tomTrue); // Force an Update
  1562. // setup caret pos
  1563. if ( !(TextMsgFilter._uKeyBoardCodePage == CP_KOREAN) || cpCursor > 0)
  1564. {
  1565. if ( cpCursor >= 0 )
  1566. {
  1567. HRESULT hResult;
  1568. int cpLocal = min(cpCursor, _cchCompStr) + _ichStart;
  1569. hResult = TextMsgFilter._pTextDoc->Range(cpLocal, cpLocal, &pTextRange);
  1570. Assert (pTextRange != NULL);
  1571. if (hResult == NO_ERROR)
  1572. {
  1573. pTextRange->Select();
  1574. pTextRange->Release();
  1575. }
  1576. TextMsgFilter._pTextDoc->Unfreeze(&lCount); // Turn on display
  1577. }
  1578. }
  1579. if (bstr)
  1580. SysFreeString(bstr);
  1581. // setup composition window for Chinese in-caret IME
  1582. if (!_fDestroy && (TextMsgFilter._uKeyBoardCodePage == CP_CHINESE_TRAD ||
  1583. TextMsgFilter._uKeyBoardCodePage == CP_CHINESE_SIM))
  1584. IMENotify ( IMN_OPENCANDIDATE, 0x01, TextMsgFilter, TRUE );
  1585. }
  1586. return S_OK; // No DefWindowProc
  1587. } // processing.
  1588. /*
  1589. * void CIme_Lev3::SetCompositionStyle (CTextMsgFilter &TextMsgFilter, CCharFormat &CF)
  1590. *
  1591. * @mfunc
  1592. * Set up a composition clause's character formmatting.
  1593. *
  1594. * @comm
  1595. * If we loaded Office's IMEShare.dll, then we ask it what the formatting
  1596. * should be, otherwise we use our own, hardwired default formatting.
  1597. *
  1598. * @devnote
  1599. * Note the use of pointers to functions when dealing with IMEShare funcs.
  1600. * This is because we dynamically load the IMEShare.dll.
  1601. *
  1602. */
  1603. void CIme_Lev3::SetCompositionStyle (
  1604. CTextMsgFilter &TextMsgFilter,
  1605. UINT attribute,
  1606. ITextFont *pTextFont)
  1607. {
  1608. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::SetCompositionStyle");
  1609. UINT ulID = 0;
  1610. COLORREF crText = UINTIMEBOGUS;
  1611. COLORREF crBackground = UINTIMEBOGUS;
  1612. BOOL fBold = FALSE;
  1613. BOOL fItalic = FALSE;
  1614. BOOL fStrikeThru = FALSE;
  1615. long lUnderlineStyle = tomNone;
  1616. if (TextMsgFilter._fRE10Mode)
  1617. {
  1618. COMPCOLOR* pcrComp = TextMsgFilter.GetIMECompAttributes();
  1619. if (!pcrComp)
  1620. goto defaultStyle;
  1621. if (attribute > ATTR_TARGET_NOTCONVERTED)
  1622. attribute = ATTR_CONVERTED;
  1623. // IME input for 1.0 mode, need to use IME Color
  1624. fBold = (pcrComp[attribute].dwEffects & CFE_BOLD);
  1625. fItalic = (pcrComp[attribute].dwEffects & CFE_ITALIC);
  1626. fStrikeThru = (pcrComp[attribute].dwEffects & CFE_STRIKEOUT);
  1627. if (pcrComp[attribute].dwEffects & CFE_UNDERLINE)
  1628. lUnderlineStyle = tomSingle;
  1629. crText = pcrComp[attribute].crText;
  1630. crBackground = pcrComp[attribute].crBackground;
  1631. }
  1632. else if (W32->HaveIMEShare())
  1633. {
  1634. CIMEShare *pIMEShare;
  1635. if (W32->getIMEShareObject(&pIMEShare))
  1636. {
  1637. if (TextMsgFilter._fUsingAIMM)
  1638. {
  1639. HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1640. if (hIMC)
  1641. {
  1642. attribute = W32->GetDisplayGUID (hIMC, attribute);
  1643. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  1644. }
  1645. }
  1646. // IMEShare 98 interface
  1647. fBold = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFBold);
  1648. fItalic = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFItalic);
  1649. if (pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareFUl))
  1650. {
  1651. ulID = pIMEShare->DwGetIMEStyle(attribute, IdstyIMEShareUKul);
  1652. if(UINTIMEBOGUS != ulID)
  1653. {
  1654. long lUnderlineCrIdx = 0;
  1655. COLORREF crUl;
  1656. // get color for underline
  1657. crUl = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubUl);
  1658. if(UINTIMEBOGUS != crUl)
  1659. {
  1660. // NOTE:- attribute is 0 based and index for EffectColor is 1 based,
  1661. HRESULT hResult = TextMsgFilter._pTextDoc->SetEffectColor(attribute+1, crUl);
  1662. // setup the high nibble for color index
  1663. if (hResult == NOERROR)
  1664. lUnderlineCrIdx = (attribute+1) << 8;
  1665. }
  1666. lUnderlineStyle = IMEShareToTomUL(ulID) + lUnderlineCrIdx;
  1667. }
  1668. }
  1669. crText = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubText);
  1670. crBackground = GetIMEShareColor(pIMEShare, attribute, IdstyIMEShareSubBack);
  1671. }
  1672. else
  1673. {
  1674. // IMEShare 96 interface
  1675. const IMESTYLE *pIMEStyle = PIMEStyleFromAttr(attribute);
  1676. if (NULL == pIMEStyle)
  1677. goto defaultStyle;
  1678. fBold = FBoldIMEStyle(pIMEStyle);
  1679. fItalic = FItalicIMEStyle(pIMEStyle);
  1680. if (FUlIMEStyle(pIMEStyle))
  1681. {
  1682. ulID = IdUlIMEStyle (pIMEStyle);
  1683. if(UINTIMEBOGUS != ulID)
  1684. lUnderlineStyle = IMEShareToTomUL(ulID);
  1685. }
  1686. crText = RGBFromIMEColorStyle(PColorStyleTextFromIMEStyle(pIMEStyle));
  1687. crBackground = RGBFromIMEColorStyle(PColorStyleBackFromIMEStyle(pIMEStyle));
  1688. }
  1689. }
  1690. else // default styles when no IMEShare.dll exist.
  1691. {
  1692. defaultStyle:
  1693. switch(attribute)
  1694. { // Apply underline style.
  1695. case ATTR_INPUT:
  1696. case ATTR_CONVERTED:
  1697. pTextFont->SetUnderline(tomDotted);
  1698. break;
  1699. case ATTR_TARGET_NOTCONVERTED:
  1700. pTextFont->SetUnderline(tomSingle);
  1701. break;
  1702. case ATTR_TARGET_CONVERTED: // Target *is* selection.
  1703. {
  1704. pTextFont->SetForeColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
  1705. pTextFont->SetBackColor(::GetSysColor(COLOR_HIGHLIGHT));
  1706. }
  1707. break;
  1708. }
  1709. return; // Done
  1710. }
  1711. // Now setup all the attribute
  1712. pTextFont->Reset(tomApplyLater);
  1713. if (fBold)
  1714. pTextFont->SetBold(tomTrue);
  1715. else if (TextMsgFilter._fRE10Mode)
  1716. pTextFont->SetBold(tomFalse);
  1717. if (fItalic)
  1718. pTextFont->SetItalic(tomTrue);
  1719. else if (TextMsgFilter._fRE10Mode)
  1720. pTextFont->SetItalic(tomFalse);
  1721. if (fStrikeThru)
  1722. pTextFont->SetStrikeThrough(tomTrue);
  1723. else if (TextMsgFilter._fRE10Mode)
  1724. pTextFont->SetStrikeThrough(tomFalse);
  1725. pTextFont->SetUnderline(lUnderlineStyle);
  1726. // ignore case where text color is same as background color
  1727. if (crText != crBackground)
  1728. {
  1729. if(UINTIMEBOGUS != crText)
  1730. pTextFont->SetForeColor(crText);
  1731. if(UINTIMEBOGUS != crBackground)
  1732. pTextFont->SetBackColor(crBackground);
  1733. }
  1734. pTextFont->Reset(tomApplyNow);
  1735. }
  1736. /*
  1737. * COLORREF CIme_Lev3::GetIMEShareColor (CIMEShare *pIMEShare, DWORD dwAttribute, DWORD dwProperty)
  1738. *
  1739. * @mfunc
  1740. * Get the IME share color for the given dwAttribute and property
  1741. *
  1742. *
  1743. * @rdesc
  1744. * COLORREF of the color
  1745. *
  1746. */
  1747. COLORREF CIme_Lev3::GetIMEShareColor(
  1748. CIMEShare *pIMEShare,
  1749. DWORD dwAttribute,
  1750. DWORD dwProperty)
  1751. {
  1752. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::GetIMEShareColor");
  1753. if (pIMEShare->DwGetIMEStyle(dwAttribute,IdstyIMEShareFSpecCol | dwProperty))
  1754. {
  1755. if (pIMEShare->DwGetIMEStyle(dwAttribute,IdstyIMEShareFSpecColText | dwProperty))
  1756. return (COLORREF) _crTextColor;
  1757. else
  1758. return (COLORREF) _crBkColor;
  1759. }
  1760. else
  1761. return (COLORREF) (pIMEShare->DwGetIMEStyle(dwAttribute,
  1762. IdstyIMEShareRGBCol | dwProperty));
  1763. }
  1764. /*
  1765. * HRESULT CIme_Lev3::IMENotify(const WPARAM wparam, const LPARAM lparam,
  1766. * CTextMsgFilter &TextMsgFilter)
  1767. *
  1768. * @mfunc
  1769. * Handle Level 3 WM_IME_NOTIFY messages.
  1770. *
  1771. * @comm
  1772. * Currently we are only interested in knowing when to update
  1773. * the n window's position.
  1774. *
  1775. * @rdesc
  1776. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  1777. */
  1778. HRESULT CIme_Lev3::IMENotify(
  1779. const WPARAM wparam, // @parm associated with message.
  1780. const LPARAM lparam, // @parm associated with message.
  1781. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  1782. BOOL fCCompWindow) // @parm Level3 Chinese Composition window
  1783. {
  1784. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMENotify");
  1785. if(IMN_OPENCANDIDATE == wparam || IMN_CLOSECANDIDATE == wparam )
  1786. {
  1787. Assert (0 != lparam);
  1788. INT index; // Candidate window index.
  1789. CANDIDATEFORM cdCandForm;
  1790. POINT ptCaret;
  1791. HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1792. if(hIMC)
  1793. {
  1794. for (index = 0; index < 32; index++) // Convert bitID to INDEX
  1795. { // because *stupid* API
  1796. if((1 << index) & lparam)
  1797. break;
  1798. }
  1799. Assert(((1 << index) & lparam) == lparam); // Only 1 set?
  1800. Assert(index < 32);
  1801. if(IMN_OPENCANDIDATE == wparam && !(TextMsgFilter._uKeyBoardCodePage == CP_KOREAN)) // Set candidate to caret.
  1802. {
  1803. HRESULT hResult;
  1804. POINT ptCurrentBottomPos;
  1805. long lTextFlow;
  1806. long lTomType = tomStart+tomClientCoord+TA_BOTTOM+TA_LEFT;
  1807. TextMsgFilter._pTextDoc->GetFEFlags(&(TextMsgFilter._lFEFlags));
  1808. lTextFlow = TextMsgFilter._lFEFlags & tomTextFlowMask;
  1809. if (lTextFlow == tomTextFlowWN)
  1810. lTomType = tomStart+tomClientCoord+TA_TOP+TA_LEFT;
  1811. GetCaretPos(&ptCaret); // Start at caret.
  1812. ptCaret.x = max(0, ptCaret.x);
  1813. ptCaret.y = max(0, ptCaret.y);
  1814. cdCandForm.dwStyle = CFS_CANDIDATEPOS;
  1815. if ( !fCCompWindow ) // Not positioning the Chinese composition
  1816. { // Window.
  1817. hResult = TextMsgFilter._pTextSel->GetPoint( lTomType,
  1818. &(ptCurrentBottomPos.x), &(ptCurrentBottomPos.y) );
  1819. if (hResult != NOERROR)
  1820. {
  1821. RECT rcArea;
  1822. // GetPoint fails, use application rect in screen coordinates
  1823. hResult = TextMsgFilter._pTextDoc->GetClientRect(tomIncludeInset+tomClientCoord,
  1824. &(rcArea.left), &(rcArea.top),
  1825. &(rcArea.right), &(rcArea.bottom));
  1826. ptCurrentBottomPos.x = ptCaret.x;
  1827. ptCurrentBottomPos.y = rcArea.bottom;
  1828. }
  1829. if (hResult == NOERROR)
  1830. {
  1831. if (TextMsgFilter._uKeyBoardCodePage == CP_JAPAN)
  1832. {
  1833. // Change style to CFS_EXCLUDE, this is to
  1834. // prevent the candidate window from covering
  1835. // the current selection.
  1836. cdCandForm.dwStyle = CFS_EXCLUDE;
  1837. cdCandForm.rcArea.left = ptCaret.x;
  1838. // FUTURE: for verticle text, need to adjust
  1839. // the rcArea to include the character width.
  1840. cdCandForm.rcArea.right = (lTextFlow == tomTextFlowNE) ? ptCurrentBottomPos.x :
  1841. cdCandForm.rcArea.left + 2;
  1842. cdCandForm.rcArea.top = ptCaret.y;
  1843. ptCaret.y = ptCurrentBottomPos.y + 4;
  1844. cdCandForm.rcArea.bottom = ptCaret.y;
  1845. }
  1846. else
  1847. ptCaret.y = ptCurrentBottomPos.y + 4;
  1848. }
  1849. }
  1850. // Most IMEs will have only 1, #0, candidate window. However, some IMEs
  1851. // may want to have a window organized alphabetically, by stroke, and
  1852. // by radical.
  1853. cdCandForm.dwIndex = index;
  1854. cdCandForm.ptCurrentPos = ptCaret;
  1855. ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM);
  1856. }
  1857. else // Reset back to CFS_DEFAULT.
  1858. {
  1859. if(ImmGetCandidateWindow(hIMC, index, &cdCandForm, TextMsgFilter._fUsingAIMM)
  1860. && CFS_DEFAULT != cdCandForm.dwStyle)
  1861. {
  1862. cdCandForm.dwStyle = CFS_DEFAULT;
  1863. ImmSetCandidateWindow(hIMC, &cdCandForm, TextMsgFilter._fUsingAIMM);
  1864. }
  1865. }
  1866. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  1867. if (TextMsgFilter._fHangulToHanja == TRUE &&
  1868. IMN_CLOSECANDIDATE == wparam &&
  1869. W32->OnWinNT4() && OnWinNTFE() && !TextMsgFilter._fUsingAIMM)
  1870. {
  1871. // By pass NT4.0 Kor Bug where we didn't get EndComposition message
  1872. // when user toggle the VK_HANJA key to terminate the reconversion.
  1873. TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_NORMAL);
  1874. }
  1875. if (IMN_CLOSECANDIDATE == wparam && CP_JAPAN == TextMsgFilter._uKeyBoardCodePage)
  1876. _fUpdateWindow = TRUE;
  1877. }
  1878. }
  1879. return S_FALSE; // Allow DefWindowProc
  1880. } // processing.
  1881. /*
  1882. *
  1883. * CIme_Lev3::IMEMouseOperation (CTextMsgFilter &TextMsgFilter, UINT msg, BOOL &fTerminateIME)
  1884. *
  1885. * @mfunc if current IME support Mouse operation, need to pass
  1886. * mouse events to IME for processing
  1887. *
  1888. * @rdesc
  1889. * BOOL-TRUE if IME handled the mouse events
  1890. * fTermineateIME-TRUE if we want to terminateIME
  1891. *
  1892. */
  1893. BOOL CIme_Lev3::IMEMouseOperation(
  1894. CTextMsgFilter &TextMsgFilter, // @parm the containing message filter.
  1895. UINT msg, // @parm message id
  1896. WPARAM wParam, // @parm wparam
  1897. BOOL &fTerminateIME) // @parm need to terminate IME
  1898. {
  1899. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMEMouseOperation");
  1900. if (IMESupportMouse(TextMsgFilter))
  1901. return TextMsgFilter.MouseOperation(msg, _ichStart, _cchCompStr, wParam, &_wParamBefore, &fTerminateIME, _hwndIME);
  1902. fTerminateIME = TRUE;
  1903. return FALSE;
  1904. }
  1905. /*
  1906. *
  1907. * CIme_Lev3::IMESupportMouse (CTextMsgFilter &TextMsgFilter)
  1908. *
  1909. * @mfunc check if current IME supports Mouse events. This should be
  1910. * a feature for IME Level 3.
  1911. *
  1912. * @comm _sIMESupportMouse is a flag with the following values:
  1913. * == 0 if we haven't checked IME mouse support
  1914. * == -1 if we have checked and IME doesn't support mouse events
  1915. * == 1 if we have checked and IME supports mouse events and we have
  1916. * retrieved the IME hWnd
  1917. */
  1918. BOOL CIme_Lev3::IMESupportMouse(
  1919. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1920. {
  1921. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::IMESupportMouse");
  1922. HIMC hIMC; // Host's IME context.
  1923. HWND hHostWnd;
  1924. long hWnd;
  1925. if (!MSIMEMouseMsg || _sIMESuportMouse == -1)
  1926. return FALSE; // No mouse operation support
  1927. if (_sIMESuportMouse == 1)
  1928. return TRUE; // IME supports mouse operation
  1929. hHostWnd = TextMsgFilter._hwnd;
  1930. if (!hHostWnd) // Windowless mode...
  1931. {
  1932. if (TextMsgFilter._pTextDoc->GetWindow(&hWnd) != S_OK || !hWnd)
  1933. return FALSE;
  1934. hHostWnd = (HWND)(DWORD_PTR)hWnd;
  1935. }
  1936. // Check if this IME supports mouse operation
  1937. hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1938. _sIMESuportMouse = -1; // Init. to no support
  1939. if(hIMC)
  1940. {
  1941. _hwndIME = ImmGetDefaultIMEWnd(hHostWnd, TextMsgFilter._fUsingAIMM);
  1942. LocalReleaseImmContext(TextMsgFilter, hIMC);
  1943. // SendMessage returns TRUE if IME supports mouse operation
  1944. if (_hwndIME && SendMessage(_hwndIME, MSIMEMouseMsg, (WPARAM)IMEMOUSE_VERSION, hIMC) )
  1945. _sIMESuportMouse = 1;
  1946. }
  1947. return (_sIMESuportMouse == 1);
  1948. }
  1949. /*
  1950. *
  1951. * CIme_Lev3::GetIMECompositionMode (CTextMsgFilter &TextMsgFilter)
  1952. *
  1953. * @mfunc Return the current IME composition mode when we have composition characters
  1954. * or when we haven't received any final string
  1955. *
  1956. */
  1957. LRESULT CIme_Lev3::GetIMECompositionMode(
  1958. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1959. {
  1960. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Lev3::GetIMECompositionMode");
  1961. if (_cchCompStr || !_fGotFinalString)
  1962. return ICM_LEVEL3;
  1963. if (!_fDestroy)
  1964. {
  1965. HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  1966. int cchCompStr = 0;
  1967. if(hIMC) // Check if there is composition string.
  1968. {
  1969. if (TextMsgFilter._fUnicodeIME)
  1970. cchCompStr = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0, TextMsgFilter._fUsingAIMM);
  1971. else
  1972. cchCompStr = ImmGetCompositionStringA(hIMC, GCS_COMPSTR, NULL, 0, TextMsgFilter._fUsingAIMM);
  1973. LocalReleaseImmContext(TextMsgFilter, hIMC);
  1974. }
  1975. if (cchCompStr)
  1976. return ICM_LEVEL3;
  1977. }
  1978. return ICM_NOTOPEN;
  1979. }
  1980. /*
  1981. * BOOL IMEHangeulToHanja (&TextMsgFilter)
  1982. *
  1983. * @func
  1984. * Initiates an IME composition string edit to convert Korean Hanguel to Hanja.
  1985. * @comm
  1986. * Called from the message loop to handle VK_KANJI_KEY.
  1987. *
  1988. * @devnote
  1989. * We decide if we need to do a conversion by checking:
  1990. * - the Fonot is a Korean font,
  1991. * - the character is a valid SBC or DBC,
  1992. * - ImmEscape accepts the character and bring up a candidate window
  1993. *
  1994. * @rdesc
  1995. * BOOL - FALSE for no conversion. TRUE if OK.
  1996. */
  1997. BOOL IMEHangeulToHanja (
  1998. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  1999. {
  2000. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "IMEHangeulToHanja");
  2001. if(!TextMsgFilter.IsIMEComposition())
  2002. {
  2003. if(TextMsgFilter._pTextSel->CanEdit(NULL) == NOERROR)
  2004. {
  2005. WCHAR szCurrentChar;
  2006. long cCurrentChar;
  2007. HRESULT hResult;
  2008. HKL hKL = GetKeyboardLayout(0x0FFFFFFFF);
  2009. HIMC hIMC;
  2010. if (!hKL)
  2011. goto Exit;
  2012. hIMC = LocalGetImmContext(TextMsgFilter);
  2013. if (!hIMC)
  2014. goto Exit;
  2015. // Collapse to cpMin
  2016. hResult = TextMsgFilter._pTextSel->Collapse(tomTrue);
  2017. // get the current character
  2018. hResult = TextMsgFilter._pTextSel->GetChar(&cCurrentChar);
  2019. if (hResult != NOERROR)
  2020. goto Exit;
  2021. szCurrentChar = (WCHAR)cCurrentChar;
  2022. // Check if the IME has a conversion for this Hangeul character.
  2023. if (ImmEscape(hKL, hIMC, IME_ESC_HANJA_MODE, (LPVOID)&szCurrentChar, TextMsgFilter._fUsingAIMM) != FALSE)
  2024. {
  2025. ITextRange *pTextRange;
  2026. POINT ptMiddlePos;
  2027. LONG cpCurrent;
  2028. hResult = TextMsgFilter._pTextSel->GetStart(&cpCurrent);
  2029. if (hResult == S_OK)
  2030. {
  2031. hResult = TextMsgFilter._pTextDoc->Range(cpCurrent, cpCurrent+1, &pTextRange);
  2032. if (hResult == S_OK && pTextRange)
  2033. {
  2034. // Check if the character is in view
  2035. if (pTextRange->GetPoint( tomEnd+TA_BASELINE+TA_LEFT,
  2036. &(ptMiddlePos.x), &(ptMiddlePos.y) ) != NOERROR)
  2037. pTextRange->ScrollIntoView(tomEnd);
  2038. pTextRange->Release();
  2039. }
  2040. }
  2041. TextMsgFilter._fHangulToHanja = TRUE;
  2042. TextMsgFilter._ime = new CIme_HangeulToHanja(TextMsgFilter);
  2043. if(TextMsgFilter.IsIMEComposition())
  2044. {
  2045. // start IME composition for the conversion
  2046. LocalReleaseImmContext(TextMsgFilter, hIMC);
  2047. return TextMsgFilter._ime->StartComposition(TextMsgFilter);
  2048. }
  2049. else
  2050. TextMsgFilter._fHangulToHanja = FALSE;
  2051. }
  2052. LocalReleaseImmContext(TextMsgFilter, hIMC);
  2053. }
  2054. }
  2055. Exit:
  2056. return S_FALSE;
  2057. }
  2058. /*
  2059. * CIme_HangeulToHanja::CIme_HangeulToHanja()
  2060. *
  2061. * @mfunc
  2062. * CIme_HangeulToHanja Constructor.
  2063. *
  2064. *
  2065. */
  2066. CIme_HangeulToHanja::CIme_HangeulToHanja(CTextMsgFilter &TextMsgFilter) :
  2067. CIme_Lev3(TextMsgFilter)
  2068. {
  2069. }
  2070. /*
  2071. * HRESULT CIme_HangeulToHanja::StartComposition(CTextMsgFilter &TextMsgFilter)
  2072. *
  2073. * @mfunc
  2074. * Begin CIme_HangeulToHanja composition string processing.
  2075. *
  2076. * @comm
  2077. * Call Level3::StartComposition. Then setup the Korean block
  2078. * caret for the Hanguel character.
  2079. *
  2080. * @rdesc
  2081. * Need to adjust _ichStart and _cchCompStr to make the Hanguel character
  2082. * "become" a composition character.
  2083. *
  2084. * @rdesc
  2085. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  2086. */
  2087. HRESULT CIme_HangeulToHanja::StartComposition(
  2088. CTextMsgFilter &TextMsgFilter ) // @parm the containing message filter.
  2089. {
  2090. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_HangeulToHanja::StartComposition");
  2091. HRESULT hr;
  2092. hr = CIme_Lev3::StartComposition(TextMsgFilter);
  2093. // initialize to 1 so Composition string will get rid of the selected Hangeul
  2094. _cchCompStr = 1;
  2095. // turn on undo
  2096. TextMsgFilter._pTextDoc->Undo(tomResume, NULL);
  2097. // Setup Block caret mode
  2098. TextMsgFilter._pTextDoc->SetCaretType(tomKoreanBlockCaret);
  2099. return hr;
  2100. }
  2101. /*
  2102. * HRESULT CIme_HangeulToHanja::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  2103. *
  2104. * @mfunc
  2105. * Handle CIme_HangeulToHanja WM_IME_COMPOSITION messages.
  2106. *
  2107. * @comm
  2108. * call CIme_Lev3::CompositionString to get rid of the selected Hanguel character,
  2109. * then setup the format for the next Composition message.
  2110. *
  2111. * @devnote
  2112. * When the next Composition message comes in and that we are no longer in IME,
  2113. * the new character will use the format as set here.
  2114. */
  2115. HRESULT CIme_HangeulToHanja::CompositionString(
  2116. const LPARAM lparam, // @parm associated with message
  2117. CTextMsgFilter &TextMsgFilter)
  2118. {
  2119. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_HangeulToHanja::CompositionString");
  2120. CIme_Lev3::CompositionString(lparam, TextMsgFilter);
  2121. return S_OK;
  2122. }
  2123. /*
  2124. * HRESULT CIme_Protected::CompositionString(const LPARAM lparam, CTextMsgFilter &TextMsgFilter)
  2125. *
  2126. * @mfunc
  2127. * Handle CIme_Protected WM_IME_COMPOSITION messages.
  2128. *
  2129. * @comm
  2130. * Just throw away the result string since we are
  2131. * in read-only or protected mode
  2132. *
  2133. *
  2134. * @rdesc
  2135. * HRESULT-S_FALSE for DefWindowProc processing, S_OK if not.
  2136. *
  2137. */
  2138. HRESULT CIme_Protected::CompositionString (
  2139. const LPARAM lparam, // @parm associated with message.
  2140. CTextMsgFilter &TextMsgFilter) // @parm the containing message filter.
  2141. {
  2142. TRACEBEGIN(TRCSUBSYSFE, TRCSCOPEINTERN, "CIme_Protected::CompositionString");
  2143. if(CLEANUP_COMPOSITION_STRING() || HAVE_RESULT_STRING()) // If result string..
  2144. {
  2145. LONG cch = 0;
  2146. HIMC hIMC = LocalGetImmContext(TextMsgFilter); // Get host's IME context.
  2147. WCHAR szCompStr[256];
  2148. if(hIMC) // Get result string.
  2149. {
  2150. cch = GetCompositionStringInfo(hIMC, GCS_RESULTSTR,
  2151. szCompStr, sizeof(szCompStr)/sizeof(szCompStr[0]),
  2152. NULL, 0, NULL, NULL, TextMsgFilter._uKeyBoardCodePage, FALSE, TextMsgFilter._fUsingAIMM);
  2153. LocalReleaseImmContext(TextMsgFilter, hIMC); // Done with IME context.
  2154. }
  2155. return NOERROR; // Don't want WM_IME_CHARs.
  2156. }
  2157. // Terminate composition to force a end composition message
  2158. TerminateIMEComposition(TextMsgFilter, CIme::TERMINATE_FORCECANCEL);
  2159. return S_FALSE;
  2160. }
  2161. #endif // NOFEPROCESSING