Source code of Windows XP (NT5)
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.

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