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.

1664 lines
38 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module CMSGFLT.CPP -- Text Message Implementation |
  5. *
  6. * Most everything to do with IME message handling.
  7. *
  8. * Original Author: <nl>
  9. * Hon Wah Chan
  10. *
  11. * History: <nl>
  12. * 2/6/98 v-honwch
  13. *
  14. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  15. */
  16. #include "_common.h"
  17. #include "_cmsgflt.h"
  18. #include "_ime.h"
  19. #define MAX_RECONVERSION_SIZE 100
  20. #define CONTROL(_ch) (_ch - 'A' + 1)
  21. /*
  22. * void CreateIMEMessageFilter(ITextMsgFilter **ppMsgFilter)
  23. *
  24. * @func
  25. * TextMsgFilter class factory.
  26. */
  27. void CreateIMEMessageFilter(ITextMsgFilter **ppMsgFilter)
  28. {
  29. CTextMsgFilter *pNewFilter = new CTextMsgFilter;
  30. *ppMsgFilter = pNewFilter ? pNewFilter : NULL;
  31. }
  32. /*
  33. * void CTextMsgFilter::~CTextMsgFilter
  34. *
  35. * @mfunc
  36. * CTextMsgFilter Destructor
  37. * Release objects being used.
  38. *
  39. */
  40. CTextMsgFilter::~CTextMsgFilter ()
  41. {
  42. if (_hIMCContext)
  43. ImmAssociateContext(_hwnd, _hIMCContext, _fUsingAIMM); // Restore IME before exit
  44. // Release various objects
  45. if (_fUsingAIMM)
  46. DeactivateAIMM();
  47. if (_pFilter)
  48. _pFilter->Release();
  49. if (_pTextSel)
  50. _pTextSel->Release();
  51. _pFilter = NULL;
  52. _pTextDoc = NULL;
  53. _pTextSel = NULL;
  54. _hwnd = NULL;
  55. _hIMCContext = NULL;
  56. }
  57. /*
  58. * STDMETHODIMP CTextMsgFilter::QueryInterface (riid, ppv)
  59. *
  60. * @mfunc
  61. * IUnknown QueryInterface support
  62. *
  63. * @rdesc
  64. * NOERROR if interface supported
  65. *
  66. */
  67. STDMETHODIMP CTextMsgFilter::QueryInterface (REFIID riid, void ** ppv)
  68. {
  69. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CTextMsgFilter::QueryInterface");
  70. if( IsEqualIID(riid, IID_IUnknown) )
  71. {
  72. *ppv = (IUnknown *)this;
  73. }
  74. else if( IsEqualIID(riid, IID_ITextMsgFilter) )
  75. {
  76. *ppv = (ITextMsgFilter *)this;
  77. }
  78. else
  79. {
  80. *ppv = NULL;
  81. return E_NOINTERFACE;
  82. }
  83. AddRef();
  84. return NOERROR;
  85. }
  86. /*
  87. * STDMETHODIMP_(ULONG) CTextMsgFilter::AddRef
  88. *
  89. * @mfunc
  90. * IUnknown AddRef support
  91. *
  92. * @rdesc
  93. * Reference count
  94. */
  95. STDMETHODIMP_(ULONG) CTextMsgFilter::AddRef()
  96. {
  97. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::AddRef");
  98. return ++_crefs;
  99. }
  100. /*
  101. * STDMETHODIMP_(ULONG) CTextMsgFilter::Release()
  102. *
  103. * @mfunc
  104. * IUnknown Release support - delete object when reference count is 0
  105. *
  106. * @rdesc
  107. * Reference count
  108. */
  109. STDMETHODIMP_(ULONG) CTextMsgFilter::Release()
  110. {
  111. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CTextMsgFilter::Release");
  112. _crefs--;
  113. if( _crefs == 0 )
  114. {
  115. delete this;
  116. return 0;
  117. }
  118. return _crefs;
  119. }
  120. /*
  121. * STDMETHODIMP_(HRESULT) CTextMsgFilter::AttachDocument(HWND, ITextDocument2)
  122. *
  123. * @mfunc
  124. * Attach message filter. Perform genral initialization
  125. *
  126. * @rdesc
  127. * NOERROR
  128. */
  129. STDMETHODIMP_(HRESULT) CTextMsgFilter::AttachDocument( HWND hwnd, ITextDocument2 *pTextDoc)
  130. {
  131. HRESULT hResult;
  132. // Cache the values for possible later use.
  133. // The TextDocument interface pointer is not AddRefed because it is a back pointer
  134. // and the lifetime of message filters is assumed to be nested inside text documents
  135. _hwnd = hwnd;
  136. _pTextDoc = pTextDoc;
  137. // Don't get selection until it is needed
  138. _pTextSel = NULL;
  139. _fUnicodeWindow = 0;
  140. if (hwnd)
  141. _fUnicodeWindow = IsWindowUnicode(hwnd);
  142. _fUsingAIMM = 0;
  143. // We will activate AIMM if it has been loaded by previous instances
  144. // NOTE: we don't support AIMM for windowless mode.
  145. if (_hwnd && IsAIMMLoaded())
  146. {
  147. // activate AIMM
  148. hResult = ActivateAIMM(FALSE);
  149. if (hResult == NOERROR)
  150. {
  151. DWORD dwAtom;
  152. ATOM aClass;
  153. // filter client windows
  154. if (dwAtom = GetClassLong(hwnd, GCW_ATOM))
  155. {
  156. aClass = dwAtom;
  157. hResult = FilterClientWindowsAIMM(&aClass, 1);
  158. }
  159. _fUsingAIMM = 1;
  160. }
  161. }
  162. // Check if current keyboard is MSIME98 or later.
  163. CheckIMEType(NULL);
  164. // Initialize some member data
  165. _fHangulToHanja = FALSE;
  166. _fIMECancelComplete = FALSE;
  167. _fIMEAlwaysNotify = FALSE;
  168. _hIMCContext = NULL;
  169. _pTextDoc->GetFEFlags(&_lFEFlags);
  170. _fRE10Mode = (_lFEFlags & tomRE10Mode);
  171. // For 1.0 mode IME color
  172. memset(_crComp, 0, sizeof(_crComp));
  173. _crComp[0].crBackground = 0x0ffffff;
  174. _crComp[0].dwEffects = CFE_UNDERLINE;
  175. _crComp[1].crBackground = 0x0808080;
  176. _crComp[2].crBackground = 0x0ffffff;
  177. _crComp[2].dwEffects = CFE_UNDERLINE;
  178. _crComp[3].crText = 0x0ffffff;
  179. _uSystemCodePage = GetACP();
  180. return NOERROR;
  181. }
  182. /*
  183. * STDMETHODIMP_(HRESULT) CTextMsgFilter::HandleMessage(UINT *, WPARAM *, LPARAM *, LRESULT *)
  184. *
  185. * @mfunc
  186. * Main Message filter message loop handling
  187. *
  188. * @rdesc
  189. * S_OK if we have handled the message
  190. * S_FALSE if we want the caller to process the message
  191. */
  192. STDMETHODIMP_(HRESULT) CTextMsgFilter::HandleMessage(
  193. UINT * pmsg,
  194. WPARAM * pwparam,
  195. LPARAM * plparam,
  196. LRESULT * plres)
  197. {
  198. HRESULT hr = S_FALSE;
  199. BOOL bReleaseSelction = FALSE;
  200. HRESULT hResult;
  201. // Give other message filters a chance to handle message
  202. // Stop with the first guy that handles the message
  203. if (_pFilter)
  204. hr = _pFilter->HandleMessage(pmsg, pwparam, plparam, plres);
  205. if (hr == S_OK)
  206. return hr;
  207. if (IsIMEComposition())
  208. {
  209. // During IME Composition, there are some messages we should
  210. // not handle. Also, there are other messages we need to handle by
  211. // terminating the IME composition first.
  212. // For WM_KEYDOWN, this is handled inside edit.c OnTxKeyDown().
  213. switch( *pmsg )
  214. {
  215. case WM_COPY:
  216. case WM_CUT:
  217. case WM_DROPFILES:
  218. case EM_REDO:
  219. case EM_SETCHARFORMAT:
  220. case WM_SETFONT:
  221. return S_OK; // Just ignore these
  222. case EM_UNDO:
  223. case WM_UNDO:
  224. // just terminate and exist for undo cases
  225. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_NORMAL);
  226. return S_OK;
  227. case WM_SETTEXT:
  228. case WM_CLEAR:
  229. case EM_STREAMIN:
  230. // these messages are used to reset our state, so reset
  231. // IME as well
  232. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_FORCECANCEL);
  233. break;
  234. case EM_SETTEXTEX:
  235. if (!_fRE10Mode) // Don't terminate if running in 10 mode
  236. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_FORCECANCEL);
  237. break;
  238. case WM_SYSKEYDOWN:
  239. // Don't terminate IME composition on VK_PROCESSKEY (F10) since Japanese
  240. // IME will process the F10 key
  241. if ( *pwparam == VK_PROCESSKEY )
  242. break;
  243. // otherwise we want to terminate the IME
  244. case EM_SETWORDBREAKPROC:
  245. case WM_PASTE:
  246. case EM_PASTESPECIAL:
  247. case EM_SCROLL:
  248. case EM_SCROLLCARET:
  249. case WM_VSCROLL:
  250. case WM_HSCROLL:
  251. case WM_KILLFOCUS:
  252. case EM_STREAMOUT:
  253. case EM_SETREADONLY:
  254. case EM_SETSEL:
  255. case EM_SETPARAFORMAT:
  256. case WM_INPUTLANGCHANGEREQUEST:
  257. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_NORMAL);
  258. break;
  259. case WM_KEYDOWN:
  260. if(GetKeyState(VK_CONTROL) & 0x8000)
  261. {
  262. // During IME Composition, there are some key events we should
  263. // not handle. Also, there are other key events we need to handle by
  264. // terminating the IME composition first.
  265. switch((WORD) *pwparam)
  266. {
  267. case VK_TAB:
  268. case VK_CLEAR:
  269. case VK_NUMPAD5:
  270. case 'A': // Ctrl-A => select all
  271. case 'C': // Ctrl-C => copy
  272. case 'X': // Ctrl-X => cut
  273. case 'Y': // Ctrl-Y => redo
  274. return S_OK; // Just ignore these
  275. case 'V': // Ctrl-V => paste
  276. case 'Z': // Ctrl-Z => undo
  277. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_NORMAL);
  278. if ((WORD) *pwparam == 'Z') // Early exist for undo case
  279. return S_OK;
  280. }
  281. }
  282. else
  283. {
  284. switch((WORD) *pwparam)
  285. {
  286. case VK_F16:
  287. return S_OK; // Just ignore these
  288. case VK_BACK:
  289. case VK_INSERT: // Ins
  290. case VK_LEFT: // Left arrow
  291. case VK_RIGHT: // Right arrow
  292. case VK_UP: // Up arrow
  293. case VK_DOWN: // Down arrow
  294. case VK_HOME: // Home
  295. case VK_END: // End
  296. case VK_PRIOR: // PgUp
  297. case VK_NEXT: // PgDn
  298. case VK_DELETE: // Del
  299. case CONTROL('J'):
  300. case VK_RETURN:
  301. _ime->TerminateIMEComposition(*this, CIme::TERMINATE_NORMAL);
  302. break;
  303. }
  304. }
  305. break;
  306. default:
  307. // only need to handle mouse related msgs during composition
  308. if (IN_RANGE(WM_MOUSEFIRST, *pmsg, WM_MBUTTONDBLCLK) || *pmsg == WM_SETCURSOR)
  309. {
  310. bReleaseSelction = GetTxSelection();
  311. if (_pTextSel)
  312. hr = IMEMouseCheck( *this, pmsg, pwparam, plparam, plres);
  313. goto Exit;
  314. }
  315. }
  316. }
  317. // Get Fe Flags for ES_NOIME or ES_SELFIME setting
  318. _lFEFlags = 0;
  319. // ... Local mucking with msg, params, etc, ...
  320. switch ( *pmsg )
  321. {
  322. case WM_CHAR:
  323. hr = OnWMChar (pmsg, pwparam, plparam, plres);
  324. break;
  325. case WM_IME_CHAR:
  326. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  327. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  328. if ((_lFEFlags & ES_NOIME))
  329. hr = S_OK;
  330. else
  331. hr = OnWMIMEChar (pmsg, pwparam, plparam, plres);
  332. break;
  333. case WM_IME_STARTCOMPOSITION:
  334. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  335. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  336. if (!(_lFEFlags & ES_SELFIME))
  337. {
  338. bReleaseSelction = GetTxSelection();
  339. if (_pTextSel)
  340. hr = StartCompositionGlue (*this);
  341. }
  342. break;
  343. case WM_IME_COMPOSITION:
  344. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  345. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  346. if ((_lFEFlags & ES_NOIME) && !IsIMEComposition())
  347. hr = S_OK;
  348. else if (!(_lFEFlags & ES_SELFIME))
  349. {
  350. bReleaseSelction = GetTxSelection();
  351. if (_pTextSel)
  352. {
  353. hr = CompositionStringGlue ( *plparam, *this );
  354. // Turn off Result string bit to avoid WM_IME_CHAR message.
  355. *plparam &= ~GCS_RESULTSTR;
  356. }
  357. }
  358. if (_hwnd && IsIMEComposition() && _ime->IgnoreIMECharMsg())
  359. {
  360. _ime->AcceptIMECharMsg();
  361. if (fHaveAIMM)
  362. hr = CallAIMMDefaultWndProc(_hwnd, *pmsg, *pwparam, *plparam, plres);
  363. else
  364. *plres = ::DefWindowProc(_hwnd, *pmsg, *pwparam, *plparam);
  365. hr = S_OK;
  366. }
  367. break;
  368. case WM_IME_ENDCOMPOSITION:
  369. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  370. if (!(_lFEFlags & ES_SELFIME))
  371. {
  372. bReleaseSelction = GetTxSelection();
  373. if (_pTextSel)
  374. hr = EndCompositionGlue ( *this, FALSE );
  375. }
  376. break;
  377. case WM_IME_NOTIFY:
  378. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  379. if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
  380. {
  381. bReleaseSelction = GetTxSelection();
  382. if (_pTextSel)
  383. hr = IMENotifyGlue ( *pwparam, *plparam, *this );
  384. }
  385. break;
  386. case WM_IME_COMPOSITIONFULL: // Level 2 comp string about to overflow.
  387. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  388. if (!(_lFEFlags & ES_SELFIME))
  389. {
  390. IMECompositionFull ( *this );
  391. }
  392. hr = S_FALSE;
  393. break;
  394. case WM_KEYDOWN:
  395. bReleaseSelction = GetTxSelection();
  396. if (_pTextSel)
  397. {
  398. if (*pwparam == VK_KANJI)
  399. {
  400. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  401. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  402. // for Korean, need to convert the next Korean Hangul character to Hanja
  403. if(CP_KOREAN == _uKeyBoardCodePage && !(_lFEFlags & (ES_SELFIME | ES_NOIME)))
  404. hr = IMEHangeulToHanja ( *this );
  405. }
  406. }
  407. break;
  408. case WM_INPUTLANGCHANGE:
  409. CheckIMEType((HKL)*plparam);
  410. hr = S_FALSE;
  411. break;
  412. case WM_KILLFOCUS:
  413. OnKillFocus();
  414. break;
  415. case WM_SETFOCUS:
  416. OnSetFocus();
  417. break;
  418. case EM_SETIMEOPTIONS:
  419. *plres = OnSetIMEOptions(*pwparam, *plparam);
  420. hr = S_OK;
  421. break;
  422. case EM_GETIMEOPTIONS:
  423. *plres = OnGetIMEOptions();
  424. hr = S_OK;
  425. break;
  426. case WM_IME_REQUEST:
  427. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  428. if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
  429. {
  430. bReleaseSelction = GetTxSelection();
  431. if (_pTextSel)
  432. {
  433. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  434. if (*pwparam == IMR_RECONVERTSTRING || *pwparam == IMR_CONFIRMRECONVERTSTRING
  435. || *pwparam == IMR_DOCUMENTFEED)
  436. hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, _fUnicodeWindow);
  437. else if (*pwparam == IMR_QUERYCHARPOSITION)
  438. hr = OnIMEQueryPos(pmsg, pwparam, plparam, plres, _fUnicodeWindow);
  439. }
  440. }
  441. break;
  442. case EM_RECONVERSION:
  443. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  444. if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
  445. {
  446. // Application initiates reconversion
  447. bReleaseSelction = GetTxSelection();
  448. if (_pTextSel)
  449. {
  450. if (!IsIMEComposition())
  451. {
  452. if (_fMSIME && MSIMEReconvertRequestMsg)
  453. // Use private message if it is available
  454. IMEMessage( *this, MSIMEReconvertRequestMsg, 0, (LPARAM)_hwnd, TRUE );
  455. else
  456. {
  457. hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, TRUE);
  458. *plres = 0;
  459. }
  460. }
  461. }
  462. }
  463. hr = S_OK;
  464. break;
  465. case EM_SETLANGOPTIONS:
  466. // Setup IME related setting.
  467. // hr is not S_OK so textserv could handle other language setting
  468. _fIMEAlwaysNotify = (*plparam & IMF_IMEALWAYSSENDNOTIFY) != 0;
  469. _fIMECancelComplete = (*plparam & IMF_IMECANCELCOMPLETE) != 0;
  470. *plres = 1;
  471. break;
  472. case EM_GETLANGOPTIONS:
  473. // Report IME related setting.
  474. // hr is not S_OK so textserv could fill in other language setting
  475. if ( _fIMECancelComplete )
  476. *plres |= IMF_IMECANCELCOMPLETE;
  477. if ( _fIMEAlwaysNotify )
  478. *plres |= IMF_IMEALWAYSSENDNOTIFY;
  479. break;
  480. case EM_GETIMECOMPMODE:
  481. // Get current IME level
  482. *plres = OnGetIMECompositionMode( *this );
  483. hr = S_OK;
  484. break;
  485. case EM_SETEDITSTYLE:
  486. if (*pwparam & SES_USEAIMM)
  487. {
  488. if (_hwnd && !_fUsingAIMM && LoadAIMM())
  489. {
  490. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  491. if (!(_lFEFlags & ES_NOIME)) // No IME style on?
  492. {
  493. // activate AIMM
  494. hResult = ActivateAIMM(FALSE);
  495. if (hResult == NOERROR)
  496. {
  497. DWORD dwAtom;
  498. ATOM aClass;
  499. // filter client windows
  500. if (dwAtom = GetClassLong(_hwnd, GCW_ATOM))
  501. {
  502. aClass = dwAtom;
  503. hResult = FilterClientWindowsAIMM(&aClass, 1);
  504. }
  505. _fUsingAIMM = 1;
  506. }
  507. }
  508. }
  509. }
  510. if ((*plparam == 0 || *plparam & SES_NOIME) && _hwnd)
  511. {
  512. if (*pwparam & SES_NOIME)
  513. {
  514. if (!_hIMCContext)
  515. _hIMCContext = ImmAssociateContext(_hwnd, NULL, _fUsingAIMM); // turn off IME
  516. }
  517. else if (*plparam & SES_NOIME)
  518. {
  519. if (_hIMCContext)
  520. ImmAssociateContext(_hwnd, _hIMCContext, _fUsingAIMM); // turn on IME
  521. _hIMCContext = NULL;
  522. }
  523. }
  524. // remove settings that are handled.
  525. *pwparam &= ~(SES_NOIME | SES_USEAIMM);
  526. *plparam &= ~(SES_NOIME | SES_USEAIMM);
  527. // fall thru to return the edit style
  528. case EM_GETEDITSTYLE:
  529. if (_hIMCContext)
  530. *plres = SES_NOIME; // IME has been turned off
  531. if (_fUsingAIMM)
  532. *plres |= SES_USEAIMM; // AIMM is on
  533. break;
  534. case EM_SETIMECOLOR:
  535. if (_fRE10Mode)
  536. {
  537. memcpy(&_crComp, (const void *)(*plparam), sizeof(_crComp));
  538. *plres = 1;
  539. }
  540. hr = S_OK;
  541. break;
  542. case EM_GETIMECOLOR:
  543. if (_fRE10Mode)
  544. {
  545. memcpy((void *)(*plparam), &_crComp, sizeof(_crComp));
  546. *plres = 1;
  547. }
  548. hr = S_OK;
  549. break;
  550. default:
  551. if (*pmsg)
  552. {
  553. // Look for IME98 private messages
  554. if (*pmsg == MSIMEReconvertMsg || *pmsg == MSIMEDocFeedMsg
  555. || *pmsg == MSIMEQueryPositionMsg)
  556. {
  557. hResult = _pTextDoc->GetFEFlags(&_lFEFlags);
  558. if (!(_lFEFlags & (ES_SELFIME | ES_NOIME)))
  559. {
  560. bReleaseSelction = GetTxSelection();
  561. if (_pTextSel)
  562. {
  563. if (*pmsg == MSIMEQueryPositionMsg)
  564. hr = OnIMEQueryPos(pmsg, pwparam, plparam, plres, TRUE);
  565. else
  566. hr = OnIMEReconvert(pmsg, pwparam, plparam, plres, TRUE);
  567. }
  568. }
  569. }
  570. }
  571. break;
  572. }
  573. Exit:
  574. // Release Selection if we get it for this message
  575. if (bReleaseSelction && _pTextSel)
  576. {
  577. _pTextSel->Release();
  578. _pTextSel = NULL;
  579. }
  580. // Return the value that will cause message to be processed normally
  581. return hr;
  582. }
  583. /*
  584. * HRESULT CTextMsgFilter::AttachMsgFilter(ITextMsgFilter *)
  585. *
  586. * @mfunc
  587. * Add another message filter to the chain
  588. *
  589. * @rdesc
  590. * NOERROR if added
  591. */
  592. HRESULT STDMETHODCALLTYPE CTextMsgFilter::AttachMsgFilter( ITextMsgFilter *pMsgFilter)
  593. {
  594. HRESULT hr = NOERROR;
  595. if (_pFilter)
  596. hr = _pFilter->AttachMsgFilter( pMsgFilter );
  597. else
  598. {
  599. _pFilter = pMsgFilter;
  600. _pFilter->AddRef();
  601. }
  602. return hr;
  603. }
  604. /*
  605. * HRESULT CTextMsgFilter::OnWMChar(UINT *, WPARAM *, LPARAM *, LRESULT *)
  606. *
  607. * @mfunc
  608. * Handle WM_CHAR message - look for Japanese keyboard with Kana key on
  609. * Convert the SB Kana to Unicode if needed.
  610. *
  611. * @rdesc
  612. * S_FALSE so caller will handle the modified character in wparam
  613. */
  614. HRESULT CTextMsgFilter::OnWMChar(
  615. UINT * pmsg,
  616. WPARAM * pwparam,
  617. LPARAM * plparam,
  618. LRESULT * plres)
  619. {
  620. // For Japanese keyboard, if Kana mode is on,
  621. // Kana characters (single byte Japanese chars) are coming in via WM_CHAR.
  622. if ( GetKeyState(VK_KANA) & 0x1 )
  623. {
  624. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  625. if (_uKeyBoardCodePage == CP_JAPAN)
  626. {
  627. // check if this is a single byte character.
  628. TCHAR unicodeConvert;
  629. BYTE bytes[2];
  630. bytes[0] = (BYTE)(*pwparam >> 8); // Interchange DBCS bytes in endian
  631. bytes[1] = (BYTE)*pwparam; // independent fashion (use byte array)
  632. if (!bytes[0])
  633. {
  634. if(UnicodeFromMbcs((LPWSTR)&unicodeConvert, 1,
  635. (LPCSTR)&bytes[1], 1, _uKeyBoardCodePage) == 1)
  636. *pwparam = unicodeConvert;
  637. }
  638. return InputFEChar(*pwparam);
  639. }
  640. }
  641. return S_FALSE;
  642. }
  643. /*
  644. * HRESULT CTextMsgFilter::OnWMIMEChar(UINT *, WPARAM *, LPARAM *, LRESULT *)
  645. *
  646. * @mfunc
  647. * Handle WM_IMECHAR message - convert the character to unicode.
  648. *
  649. * @rdesc
  650. * S_OK - caller to ignore the message
  651. * S_FALSE - caller to handle the message. wparam may contains a new char
  652. */
  653. HRESULT CTextMsgFilter::OnWMIMEChar(
  654. UINT * pmsg,
  655. WPARAM * pwparam,
  656. LPARAM * plparam,
  657. LRESULT * plres)
  658. {
  659. TCHAR unicodeConvert;
  660. BYTE bytes[2];
  661. // We may receive IMECHAR even if we have handled the composition char already.
  662. // This is the case when the host does not call the DefWinProc with the composition
  663. // bit masked off. So, we need to ignore this message to avoid double entry.
  664. if (IsIMEComposition() && _ime->IgnoreIMECharMsg())
  665. {
  666. _ime->SkipIMECharMsg(); // Skip this ime char msg
  667. return S_OK;
  668. }
  669. bytes[0] = *pwparam >> 8; // Interchange DBCS bytes in endian
  670. bytes[1] = *pwparam; // independent fashion (use byte array)
  671. // need to convert both single-byte KANA and DBC
  672. if (!bytes[0] || GetTrailBytesCount(bytes[0], _uKeyBoardCodePage))
  673. {
  674. if( UnicodeFromMbcs((LPWSTR)&unicodeConvert, 1,
  675. bytes[0] == 0 ? (LPCSTR)&bytes[1] : (LPCSTR)bytes,
  676. bytes[0] == 0 ? 1 : 2,
  677. _uKeyBoardCodePage) == 1 )
  678. *pwparam = unicodeConvert;
  679. return InputFEChar(*pwparam);
  680. }
  681. return S_FALSE;
  682. }
  683. /*
  684. * HRESULT CTextMsgFilter::OnIMEReconvert(UINT *, WPARAM *, LPARAM *, LRESULT *)
  685. *
  686. * @mfunc
  687. * Handle IME Reconversion and Document feed. We only handle Unicode messages.
  688. * We use a limit of MAX_RECONVERSION_SIZE(100) characters in both cases.
  689. *
  690. * @rdesc
  691. * S_OK if we have handled the message
  692. */
  693. HRESULT CTextMsgFilter::OnIMEReconvert(
  694. UINT * pmsg,
  695. WPARAM * pwparam,
  696. LPARAM * plparam,
  697. LRESULT * plres,
  698. BOOL fUnicode)
  699. {
  700. HRESULT hr = S_OK;
  701. LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)(*plparam);
  702. long cbStringSize;
  703. long cpMin, cpMax;
  704. long cpParaStart, cpParaEnd;
  705. HRESULT hResult;
  706. ITextRange *pTextRange, *pTempTextRange;
  707. long cbAdded;
  708. BOOL bDocumentFeed;
  709. long cLastChar;
  710. BOOL fAdjustedRange = FALSE;
  711. *plres = 0;
  712. // NT doesn't support Ansi window when the CP_ACP isn't the same
  713. // as keyboard codepage.
  714. if (!fUnicode && !(W32->OnWin9x()) && _uKeyBoardCodePage != _uSystemCodePage)
  715. return S_OK;
  716. bDocumentFeed = (MSIMEDocFeedMsg && *pmsg == MSIMEDocFeedMsg)
  717. || (*pmsg == WM_IME_REQUEST && *pwparam == IMR_DOCUMENTFEED);
  718. if (bDocumentFeed && IsIMEComposition() && _ime->GetIMELevel() == IME_LEVEL_3)
  719. {
  720. // Composition in progress, use composition string as selection
  721. cpMin = ((CIme_Lev3 *)_ime)->GetIMECompositionStart();
  722. cpMax = ((CIme_Lev3 *)_ime)->GetIMECompositionLen() + cpMin;
  723. }
  724. else
  725. {
  726. // Get current selection
  727. hResult = _pTextSel->GetStart(&cpMin);
  728. hResult = _pTextSel->GetEnd(&cpMax);
  729. }
  730. // Expand to include the current paragraph
  731. hResult = _pTextDoc->Range(cpMin, cpMax, &pTextRange);
  732. Assert (pTextRange != NULL);
  733. if (hResult != NOERROR)
  734. return S_OK;
  735. hResult = pTextRange->Expand(tomParagraph, &cbAdded);
  736. // Fail to get Paragraph, get the story
  737. // Note:- Expand will return S_FALSE for plain text when
  738. // the whole story is selected
  739. if (hResult != NOERROR)
  740. hResult = pTextRange->Expand(tomStory, &cbAdded);
  741. hResult = pTextRange->GetStart(&cpParaStart);
  742. hResult = pTextRange->GetEnd(&cpParaEnd);
  743. if (*pwparam == IMR_CONFIRMRECONVERTSTRING)
  744. {
  745. *plres = CheckIMEChange(lpRCS, cpParaStart, cpParaEnd, cpMin, cpMax, fUnicode);
  746. goto Exit;
  747. }
  748. // Initialize to hugh number
  749. _cpReconvertStart = tomForward;
  750. // Check if Par included
  751. hResult = _pTextDoc->Range(cpParaEnd-1, cpParaEnd, &pTempTextRange);
  752. if (hResult != NOERROR)
  753. goto Exit;
  754. Assert (pTempTextRange != NULL);
  755. hResult = pTempTextRange->GetChar(&cLastChar);
  756. pTempTextRange->Release();
  757. if (hResult == NOERROR && (WCHAR)cLastChar == CR)
  758. {
  759. if (cpMax == cpParaEnd)
  760. {
  761. // Par is selected, change selection to exclude the par char
  762. cpMax--;
  763. _pTextSel->SetEnd(cpMax);
  764. if (cpMin > cpMax)
  765. {
  766. // Adjust cpMin as well
  767. cpMin = cpMax;
  768. _pTextSel->SetStart(cpMin);
  769. }
  770. }
  771. // Get rid of par char
  772. cpParaEnd--;
  773. fAdjustedRange = TRUE;
  774. }
  775. // Check for MAX_RECONVERSION_SIZE since we don't want to pass a hugh buffer
  776. // to IME
  777. long cchSelected;
  778. cchSelected = cpMax - cpMin;
  779. if (cpParaEnd - cpParaStart > MAX_RECONVERSION_SIZE)
  780. {
  781. // Too many character selected, forget it
  782. if (cchSelected > MAX_RECONVERSION_SIZE)
  783. goto Exit;
  784. if (cchSelected == MAX_RECONVERSION_SIZE)
  785. {
  786. // Selection reaches the limit
  787. cpParaStart = cpMin;
  788. cpParaEnd = cpMax;
  789. }
  790. else
  791. {
  792. long cchBeforeSelection = cpMin - cpParaStart;
  793. long cchAfterSelection = cpParaEnd - cpMax;
  794. long cchNeeded = MAX_RECONVERSION_SIZE - cchSelected;
  795. if (cchBeforeSelection < cchNeeded/2)
  796. {
  797. // Put in all characters from the Par start
  798. // and move Par end
  799. cpParaEnd = cpParaStart + MAX_RECONVERSION_SIZE - 1;
  800. }
  801. else if (cchAfterSelection < cchNeeded/2)
  802. {
  803. // Put in all character to the Par end
  804. // and move Par start
  805. cpParaStart = cpParaEnd - MAX_RECONVERSION_SIZE + 1;
  806. }
  807. else
  808. {
  809. // Adjust both end
  810. cpParaStart = cpMin - cchNeeded/2;
  811. cpParaEnd = cpParaStart + MAX_RECONVERSION_SIZE - 1;
  812. }
  813. }
  814. fAdjustedRange = TRUE;
  815. }
  816. if (fAdjustedRange)
  817. {
  818. // Adjust the text range
  819. hResult = pTextRange->SetRange(cpParaStart, cpParaEnd);
  820. if (hResult != NOERROR)
  821. goto Exit;
  822. }
  823. cbStringSize = (cpParaEnd - cpParaStart) * 2;
  824. // No char in current par, forget it.
  825. if (cbStringSize <= 0)
  826. goto Exit;
  827. if (EM_RECONVERSION == *pmsg)
  828. {
  829. // RE reconversion msg, allocate the Reconversion buffer
  830. lpRCS = (LPRECONVERTSTRING) PvAlloc(sizeof(RECONVERTSTRING) + cbStringSize + 2, GMEM_ZEROINIT);
  831. Assert(lpRCS != NULL);
  832. if (lpRCS)
  833. lpRCS->dwSize = sizeof(RECONVERTSTRING) + cbStringSize + 2;
  834. }
  835. if (lpRCS)
  836. {
  837. BSTR bstr = NULL;
  838. LPSTR lpReconvertBuff;
  839. hResult = pTextRange->GetText(&bstr);
  840. if (hResult != NOERROR || bstr == NULL)
  841. {
  842. if (EM_RECONVERSION == *pmsg)
  843. FreePv(lpRCS);
  844. goto Exit; // forget it
  845. }
  846. if (lpRCS->dwSize - sizeof(RECONVERTSTRING) - 2 < (DWORD)cbStringSize)
  847. cbStringSize = lpRCS->dwSize - sizeof(RECONVERTSTRING) - 2;
  848. lpReconvertBuff = (LPSTR)(lpRCS) + sizeof(RECONVERTSTRING);
  849. if (fUnicode)
  850. {
  851. // fill in the buffer
  852. memcpy(lpReconvertBuff, (LPSTR)bstr, cbStringSize);
  853. *(lpReconvertBuff+cbStringSize) = '\0';
  854. *(lpReconvertBuff+cbStringSize+1) = '\0';
  855. lpRCS->dwStrLen = (cpParaEnd - cpParaStart);
  856. lpRCS->dwCompStrLen = (cpMax - cpMin);
  857. lpRCS->dwCompStrOffset = (cpMin - cpParaStart)*2; // byte offset from beginning of string
  858. }
  859. else
  860. {
  861. // Ansi case, need to find byte offset and Ansi string
  862. long cch = WideCharToMultiByte(_uKeyBoardCodePage, 0, bstr, -1, lpReconvertBuff, cbStringSize+1, NULL, NULL);
  863. Assert (cch > 0);
  864. if (cch > 0)
  865. {
  866. CTempCharBuf tcb;
  867. char *psz = tcb.GetBuf(cch);
  868. if (cch > 1 && lpReconvertBuff[cch-1] == '\0')
  869. cch--; // Get rid of the null char
  870. lpRCS->dwStrLen = cch;
  871. lpRCS->dwCompStrOffset = WideCharToMultiByte(_uKeyBoardCodePage, 0,
  872. bstr, cpMin - cpParaStart, psz, cch, NULL, NULL);
  873. lpRCS->dwCompStrLen = 0;
  874. if (cpMax > cpMin)
  875. lpRCS->dwCompStrLen = WideCharToMultiByte(_uKeyBoardCodePage, 0,
  876. bstr+cpMin, cpMax - cpMin, psz, cch, NULL, NULL);
  877. }
  878. else
  879. {
  880. SysFreeString (bstr);
  881. if (EM_RECONVERSION == *pmsg)
  882. FreePv(lpRCS);
  883. goto Exit; // forget it
  884. }
  885. }
  886. // Fill in the rest of the RCS struct
  887. lpRCS->dwVersion = 0;
  888. lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); // byte offset from beginning of struct
  889. lpRCS->dwTargetStrLen = lpRCS->dwCompStrLen;
  890. lpRCS->dwTargetStrOffset = lpRCS->dwCompStrOffset;
  891. *plres = sizeof(RECONVERTSTRING) + cbStringSize + 2;
  892. // Save this for the CONFIRMRECONVERTSTRING handling
  893. _cpReconvertStart = cpParaStart;
  894. _cpReconvertEnd = cpParaEnd;
  895. SysFreeString (bstr);
  896. if (EM_RECONVERSION == *pmsg)
  897. {
  898. HIMC hIMC = ImmGetContext(_hwnd);
  899. if (hIMC)
  900. {
  901. DWORD imeProperties = ImmGetProperty(GetKeyboardLayout(0x0FFFFFFFF), IGP_SETCOMPSTR, _fUsingAIMM);
  902. if ((imeProperties & (SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD))
  903. == (SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD))
  904. {
  905. if (ImmSetCompositionStringW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, *plres, NULL, 0))
  906. {
  907. // Check if there is any change in selection
  908. CheckIMEChange(lpRCS, cpParaStart, cpParaEnd, cpMin, cpMax, TRUE);
  909. ImmSetCompositionStringW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, *plres, NULL, 0);
  910. }
  911. }
  912. ImmReleaseContext(_hwnd, hIMC);
  913. }
  914. FreePv(lpRCS);
  915. }
  916. }
  917. else
  918. {
  919. // return size for IME to allocate the buffer
  920. *plres = sizeof(RECONVERTSTRING) + cbStringSize + 2;
  921. }
  922. Exit:
  923. pTextRange->Release();
  924. return hr;
  925. }
  926. /*
  927. * BOOL CTextMsgFilter::CheckIMEChange(LPRECONVERTSTRING,long,long,long,long)
  928. *
  929. * @mfunc
  930. * Verify if IME wants to re-adjust the selection
  931. *
  932. * @rdesc
  933. * TRUE - allow IME to change the selection
  934. */
  935. BOOL CTextMsgFilter::CheckIMEChange(
  936. LPRECONVERTSTRING lpRCS,
  937. long cpParaStart,
  938. long cpParaEnd,
  939. long cpMin,
  940. long cpMax,
  941. BOOL fUnicode)
  942. {
  943. long cpImeSelectStart = 0;
  944. long cpImeSelectEnd = 0;
  945. HRESULT hResult;
  946. if (!lpRCS || _cpReconvertStart == tomForward)
  947. // Never initialize, forget it
  948. return FALSE;
  949. if (fUnicode)
  950. {
  951. cpImeSelectStart = _cpReconvertStart + lpRCS->dwCompStrOffset / 2;
  952. cpImeSelectEnd = cpImeSelectStart + lpRCS->dwCompStrLen;
  953. }
  954. else
  955. {
  956. // Need to convert the byte offset to char offset.
  957. ITextRange *pTextRange;
  958. BSTR bstr = NULL;
  959. hResult = _pTextDoc->Range(_cpReconvertStart, _cpReconvertEnd, &pTextRange);
  960. if (hResult != NOERROR)
  961. return FALSE;
  962. // Get the text
  963. hResult = pTextRange->GetText(&bstr);
  964. if (hResult == S_OK)
  965. {
  966. long cchReconvert = _cpReconvertEnd - _cpReconvertStart + 1;
  967. CTempCharBuf tcb;
  968. char *psz = tcb.GetBuf((cchReconvert)*2);
  969. long cch = WideCharToMultiByte(_uKeyBoardCodePage, 0,
  970. bstr, -1, psz, (cchReconvert)*2, NULL, NULL);
  971. if (cch > 0)
  972. {
  973. long dwCompStrOffset, dwCompStrLen;
  974. CTempWcharBuf twcb;
  975. WCHAR *pwsz = twcb.GetBuf(cchReconvert);
  976. dwCompStrOffset = MultiByteToWideChar(_uKeyBoardCodePage, 0,
  977. psz, lpRCS->dwCompStrOffset, pwsz, cchReconvert);
  978. dwCompStrLen = MultiByteToWideChar(_uKeyBoardCodePage, 0,
  979. psz+lpRCS->dwCompStrOffset, lpRCS->dwCompStrLen, pwsz, cchReconvert);
  980. Assert(dwCompStrOffset > 0 || dwCompStrLen > 0);
  981. cpImeSelectStart = _cpReconvertStart + dwCompStrOffset;
  982. cpImeSelectEnd = cpImeSelectStart + dwCompStrLen;
  983. }
  984. else
  985. hResult = S_FALSE;
  986. }
  987. if (bstr)
  988. SysFreeString (bstr);
  989. pTextRange->Release();
  990. if (hResult != S_OK)
  991. return FALSE;
  992. }
  993. if (cpParaStart <= cpImeSelectStart && cpImeSelectEnd <= cpParaEnd)
  994. {
  995. if (_pTextSel && (cpImeSelectStart != cpMin || cpImeSelectEnd != cpMax))
  996. {
  997. // IME changes selection.
  998. hResult = _pTextSel->SetRange(cpImeSelectStart, cpImeSelectEnd);
  999. if (hResult != NOERROR)
  1000. return FALSE;
  1001. }
  1002. return TRUE; // Allow Ime to change selection
  1003. }
  1004. return FALSE;
  1005. }
  1006. /*
  1007. * BOOL CTextMsgFilter::GetTxSelection()
  1008. *
  1009. * @mfunc
  1010. * Get Selection if we haven't got it before
  1011. *
  1012. * @rdesc
  1013. * TRUE if this is first time getting the selection
  1014. * FALSE if it is already exist or no selection available.
  1015. */
  1016. BOOL CTextMsgFilter::GetTxSelection()
  1017. {
  1018. HRESULT hResult;
  1019. if (_pTextSel)
  1020. return FALSE; // Already there
  1021. hResult = _pTextDoc->GetSelectionEx(&_pTextSel);
  1022. return _pTextSel ? TRUE : FALSE;
  1023. }
  1024. /*
  1025. * HRESULT CTextMsgFilter::OnIMEQueryPos(UINT *, WPARAM *, LPARAM *, LRESULT *, BOOL)
  1026. *
  1027. * @mfunc
  1028. * Fill in the current character size and window rect. size.
  1029. *
  1030. * @rdesc
  1031. * S_OK
  1032. * *plres = 0 if we do not filled in data
  1033. */
  1034. HRESULT CTextMsgFilter::OnIMEQueryPos(
  1035. UINT * pmsg,
  1036. WPARAM * pwparam,
  1037. LPARAM * plparam,
  1038. LRESULT * plres,
  1039. BOOL fUnicode)
  1040. {
  1041. HRESULT hResult;
  1042. PIMECHARPOSITION pIMECharPos = (PIMECHARPOSITION)*plparam;
  1043. long cpRequest;
  1044. RECT rcArea;
  1045. ITextRange *pTextRange = NULL;
  1046. POINT ptTopPos, ptBottomPos = {0, 0};
  1047. bool fGetBottomPosFail = false;
  1048. if (pIMECharPos->dwSize != sizeof(IMECHARPOSITION))
  1049. goto Exit;
  1050. // NT doesn't support Ansi window when the CP_ACP isn't the same
  1051. // as keyboard codepage.
  1052. if (!fUnicode && !(W32->OnWin9x()) && _uKeyBoardCodePage != _uSystemCodePage)
  1053. goto Exit;
  1054. if (IsIMEComposition() && _ime->GetIMELevel() == IME_LEVEL_3)
  1055. {
  1056. cpRequest = ((CIme_Lev3 *)_ime)->GetIMECompositionStart();
  1057. if (fUnicode)
  1058. cpRequest += pIMECharPos->dwCharPos;
  1059. else if (pIMECharPos->dwCharPos > 0)
  1060. {
  1061. // Need to convert pIMECharPos->dwCharPos from Acp to Cp
  1062. long cchComp = ((CIme_Lev3 *)_ime)->GetIMECompositionLen();
  1063. long cchAcp = (long)(pIMECharPos->dwCharPos);
  1064. BSTR bstr;
  1065. WCHAR *pChar;
  1066. if (cchComp)
  1067. {
  1068. hResult = _pTextDoc->Range(cpRequest, cpRequest+cchComp, &pTextRange);
  1069. Assert (pTextRange != NULL);
  1070. if (hResult != NOERROR || !pTextRange)
  1071. goto Exit;
  1072. hResult = pTextRange->GetText(&bstr);
  1073. if (hResult != NOERROR )
  1074. goto Exit;
  1075. // The algorithm assumes that for a DBCS charset any character
  1076. // above 128 has two bytes, except for the halfwidth KataKana,
  1077. // which are single bytes in ShiftJis.
  1078. pChar = (WCHAR *)bstr;
  1079. Assert (pChar);
  1080. while (cchAcp > 0 && cchComp > 0)
  1081. {
  1082. cchAcp--;
  1083. if(*pChar >= 128 && (CP_JAPAN != _uKeyBoardCodePage ||
  1084. !IN_RANGE(0xFF61, *pChar, 0xFF9F)))
  1085. cchAcp--;
  1086. pChar++;
  1087. cchComp--;
  1088. cpRequest++;
  1089. }
  1090. SysFreeString (bstr);
  1091. pTextRange->Release();
  1092. pTextRange = NULL;
  1093. }
  1094. }
  1095. }
  1096. else if (pIMECharPos->dwCharPos == 0)
  1097. {
  1098. // Get current selection
  1099. hResult = _pTextSel->GetStart(&cpRequest);
  1100. if (hResult != NOERROR)
  1101. goto Exit;
  1102. }
  1103. else
  1104. goto Exit;
  1105. // Get requested cp location in screen coordinates
  1106. hResult = _pTextDoc->Range(cpRequest, cpRequest+1, &pTextRange);
  1107. Assert (pTextRange != NULL);
  1108. if (hResult != NOERROR || !pTextRange)
  1109. goto Exit;
  1110. hResult = pTextRange->GetPoint( tomStart+TA_TOP+TA_LEFT,
  1111. &(ptTopPos.x), &(ptTopPos.y) );
  1112. if (hResult != NOERROR)
  1113. {
  1114. // Scroll and try again
  1115. hResult = pTextRange->ScrollIntoView(tomStart);
  1116. if (hResult == NOERROR)
  1117. hResult = pTextRange->GetPoint( tomStart+TA_TOP+TA_LEFT,
  1118. &(ptTopPos.x), &(ptTopPos.y) );
  1119. }
  1120. if (hResult == NOERROR)
  1121. {
  1122. hResult = pTextRange->GetPoint( tomStart+TA_BOTTOM+TA_LEFT,
  1123. &(ptBottomPos.x), &(ptBottomPos.y) );
  1124. if (hResult != NOERROR)
  1125. fGetBottomPosFail = true;
  1126. }
  1127. pIMECharPos->pt = ptTopPos;
  1128. // Get application rect in screen coordinates
  1129. hResult = _pTextDoc->GetClientRect(tomIncludeInset,
  1130. &(rcArea.left), &(rcArea.top),
  1131. &(rcArea.right), &(rcArea.bottom));
  1132. if (hResult != NOERROR)
  1133. goto Exit;
  1134. // Get line height in pixel
  1135. if (fGetBottomPosFail)
  1136. pIMECharPos->cLineHeight = rcArea.bottom - ptTopPos.y;
  1137. else
  1138. pIMECharPos->cLineHeight = ptBottomPos.y - ptTopPos.y;
  1139. pIMECharPos->rcDocument = rcArea;
  1140. *plres = TRUE;
  1141. Exit:
  1142. if (pTextRange)
  1143. pTextRange->Release();
  1144. return S_OK;
  1145. }
  1146. /*
  1147. * CTextMsgFilter::CheckIMEType(HKL hKL)
  1148. *
  1149. * @mfunc
  1150. * Check for MSIME98 or later
  1151. *
  1152. */
  1153. void CTextMsgFilter::CheckIMEType(
  1154. HKL hKL)
  1155. {
  1156. if (!hKL)
  1157. hKL = GetKeyboardLayout(0x0FFFFFFFF); // Get default HKL if caller pass in NULL
  1158. // initialize to non MS IME
  1159. _fMSIME = 0;
  1160. if (IsFELCID((WORD)hKL))
  1161. {
  1162. // Check what kind of IME user selected
  1163. if (MSIMEServiceMsg && IMEMessage( *this, MSIMEServiceMsg, 0, 0, FALSE ))
  1164. _fMSIME = 1;
  1165. }
  1166. }
  1167. /*
  1168. * CTextMsgFilter::InputFEChar(WCHAR wchFEChar)
  1169. *
  1170. * @mfunc
  1171. * Input the FE character and ensure we have a correct font.
  1172. *
  1173. * @rdesc
  1174. * S_OK if handled
  1175. */
  1176. HRESULT CTextMsgFilter::InputFEChar(
  1177. WCHAR wchFEChar)
  1178. {
  1179. BOOL bReleaseSelction = GetTxSelection();
  1180. long cchExced;
  1181. HRESULT hr = S_FALSE;
  1182. if (wchFEChar > 256
  1183. && _pTextSel->CanEdit(NULL) == NOERROR
  1184. && _pTextDoc->CheckTextLimit(1, &cchExced) == NOERROR
  1185. && cchExced == 0)
  1186. {
  1187. // setup FE font to handle the FE character
  1188. long cpMin, cpMax;
  1189. TCHAR wchFE[2];
  1190. BOOL fSelect = FALSE;
  1191. ITextRange *pTextRange = NULL;
  1192. ITextFont *pTextFont = NULL;
  1193. ITextFont *pFEFont = NULL;
  1194. HRESULT hResult = S_FALSE;
  1195. BSTR bstr = NULL;
  1196. // Inform client IME compostion is on to by-pass some font setting
  1197. // problem in Arabic systems
  1198. _pTextDoc->IMEInProgress(tomTrue);
  1199. wchFE[0] = wchFEChar;
  1200. wchFE[1] = L'\0';
  1201. _pTextSel->GetStart(&cpMin);
  1202. _pTextSel->GetEnd(&cpMax);
  1203. // For selection case, we want font to the right of first character
  1204. if (cpMin != cpMax)
  1205. {
  1206. hResult = _pTextDoc->Range(cpMin, cpMin, &pTextRange);
  1207. if (hResult != S_OK)
  1208. goto ERROR_EXIT;
  1209. hResult = pTextRange->GetFont(&pTextFont);
  1210. cpMin++;
  1211. fSelect = TRUE;
  1212. }
  1213. else
  1214. hResult = _pTextSel->GetFont(&pTextFont);
  1215. // Get a duplicate font and setup the correct FE font
  1216. hResult = pTextFont->GetDuplicate(&pFEFont);
  1217. if (hResult != S_OK)
  1218. goto ERROR_EXIT;
  1219. CIme::CheckKeyboardFontMatching (cpMin, *this, pFEFont);
  1220. if (fSelect)
  1221. _pTextSel->SetText(NULL); // Delete the selection
  1222. bstr = SysAllocString(wchFE);
  1223. if (!bstr)
  1224. {
  1225. hResult = E_OUTOFMEMORY;
  1226. goto ERROR_EXIT;
  1227. }
  1228. _pTextSel->SetFont(pFEFont); // Setup FE font
  1229. _pTextSel->TypeText(bstr); // Input the new FE character
  1230. ERROR_EXIT:
  1231. if (hResult == S_OK)
  1232. hr = S_OK;
  1233. if (pFEFont)
  1234. pFEFont->Release();
  1235. if (pTextFont)
  1236. pTextFont->Release();
  1237. if (pTextRange)
  1238. pTextRange->Release();
  1239. if (bstr)
  1240. SysFreeString(bstr);
  1241. // Inform client IME compostion is done
  1242. _pTextDoc->IMEInProgress(tomFalse);
  1243. }
  1244. if (bReleaseSelction && _pTextSel)
  1245. {
  1246. _pTextSel->Release();
  1247. _pTextSel = NULL;
  1248. }
  1249. return hr;
  1250. }
  1251. /*
  1252. * CTextMsgFilter::OnSetFocus()
  1253. *
  1254. * @mfunc
  1255. * Restore the previous keyboard if we are in FORCEREMEMBER mode.
  1256. * Otherwise, setup the FE keyboard.
  1257. *
  1258. */
  1259. void CTextMsgFilter::OnSetFocus()
  1260. {
  1261. if (!_hwnd)
  1262. return;
  1263. if (_fForceRemember && _fIMEHKL)
  1264. {
  1265. // Restore previous keyboard
  1266. ActivateKeyboardLayout(_fIMEHKL, 0);
  1267. if (IsFELCID((WORD)_fIMEHKL))
  1268. {
  1269. // Set Open status and Conversion mode
  1270. HIMC hIMC = ImmGetContext(_hwnd);
  1271. if (hIMC)
  1272. {
  1273. if (ImmSetOpenStatus(hIMC, _fIMEEnable, _fUsingAIMM) && _fIMEEnable)
  1274. ImmSetConversionStatus(hIMC, _fIMEConversion, _fIMESentence, _fUsingAIMM); // Set conversion status
  1275. ImmReleaseContext(_hwnd, hIMC);
  1276. }
  1277. }
  1278. }
  1279. else
  1280. SetupIMEOptions();
  1281. }
  1282. /*
  1283. * CTextMsgFilter::OnKillFocus()
  1284. *
  1285. * @mfunc
  1286. * If we are in FORCE_REMEMBER mode, save the current keyboard
  1287. * and conversion setting.
  1288. *
  1289. */
  1290. void CTextMsgFilter::OnKillFocus()
  1291. {
  1292. if (!_hwnd)
  1293. return;
  1294. if (_fForceRemember)
  1295. {
  1296. // Get current keyboard
  1297. _fIMEHKL = GetKeyboardLayout(0x0FFFFFFFF);
  1298. if (IsFELCID((WORD)_fIMEHKL))
  1299. {
  1300. // Get Open status
  1301. HIMC hIMC = ImmGetContext(_hwnd);
  1302. if (hIMC)
  1303. {
  1304. _fIMEEnable = ImmGetOpenStatus(hIMC, _fUsingAIMM);
  1305. if (_fIMEEnable)
  1306. ImmGetConversionStatus(hIMC, &_fIMEConversion, &_fIMESentence, _fUsingAIMM); // get conversion status
  1307. ImmReleaseContext(_hwnd, hIMC);
  1308. }
  1309. }
  1310. }
  1311. }
  1312. /*
  1313. * CTextMsgFilter::OnSetIMEOptions(WPARAM wparam, LPARAM lparam)
  1314. *
  1315. * @mfunc
  1316. *
  1317. * @rdesc
  1318. */
  1319. LRESULT CTextMsgFilter::OnSetIMEOptions(
  1320. WPARAM wparam,
  1321. LPARAM lparam)
  1322. {
  1323. LRESULT lIMEOptionCurrent = OnGetIMEOptions();
  1324. LRESULT lIMEOptionNew = 0;
  1325. // Mask off bits that we will support for now
  1326. lparam &= (IMF_FORCEACTIVE | IMF_FORCEENABLE | IMF_FORCEREMEMBER);
  1327. switch(wparam)
  1328. {
  1329. case ECOOP_SET:
  1330. lIMEOptionNew = lparam;
  1331. break;
  1332. case ECOOP_OR:
  1333. lIMEOptionNew = lIMEOptionCurrent | lparam;
  1334. break;
  1335. case ECOOP_AND:
  1336. lIMEOptionNew = lIMEOptionCurrent & lparam;
  1337. break;
  1338. case ECOOP_XOR:
  1339. lIMEOptionNew = lIMEOptionCurrent ^ lparam;
  1340. break;
  1341. default:
  1342. return 0; // Bad option
  1343. }
  1344. if (lIMEOptionNew == lIMEOptionCurrent) // Nothing change
  1345. return 1;
  1346. _fForceActivate = FALSE;
  1347. if (lIMEOptionNew & IMF_FORCEACTIVE)
  1348. _fForceActivate = TRUE;
  1349. _fForceEnable = FALSE;
  1350. if (lIMEOptionNew & IMF_FORCEENABLE)
  1351. _fForceEnable = TRUE;
  1352. _fForceRemember = FALSE;
  1353. if (lIMEOptionNew & IMF_FORCEREMEMBER)
  1354. _fForceRemember = TRUE;
  1355. SetupIMEOptions();
  1356. return 1;
  1357. }
  1358. /*
  1359. * CTextMsgFilter::OnGetIMEOptions()
  1360. *
  1361. * @mfunc
  1362. *
  1363. * @rdesc
  1364. */
  1365. LRESULT CTextMsgFilter::OnGetIMEOptions()
  1366. {
  1367. LRESULT lres = 0;
  1368. if (_fForceActivate)
  1369. lres |= IMF_FORCEACTIVE;
  1370. if (_fForceEnable)
  1371. lres |= IMF_FORCEENABLE;
  1372. if (_fForceRemember)
  1373. lres |= IMF_FORCEREMEMBER;
  1374. return lres;
  1375. }
  1376. /*
  1377. * CTextMsgFilter::SetupIMEOptions()
  1378. *
  1379. * @mfunc
  1380. *
  1381. */
  1382. void CTextMsgFilter::SetupIMEOptions()
  1383. {
  1384. if (!_hwnd)
  1385. return;
  1386. _uKeyBoardCodePage = GetKeyboardCodePage(0x0FFFFFFFF);
  1387. if (_fForceEnable)
  1388. {
  1389. LONG cpgLocale = GetACP();
  1390. BYTE bCharSet = (BYTE)GetCharSet(cpgLocale);
  1391. if (W32->IsFECodePage(cpgLocale))
  1392. {
  1393. if (_uKeyBoardCodePage != (UINT)cpgLocale)
  1394. W32->CheckChangeKeyboardLayout(bCharSet);
  1395. HIMC hIMC = ImmGetContext(_hwnd);
  1396. if (hIMC)
  1397. {
  1398. if (ImmSetOpenStatus(hIMC, TRUE, _fUsingAIMM) && _fForceActivate)
  1399. {
  1400. // Activate native input mode
  1401. DWORD dwConversion;
  1402. DWORD dwSentence;
  1403. if (ImmGetConversionStatus(hIMC, &dwConversion, &dwSentence, _fUsingAIMM))
  1404. {
  1405. dwConversion |= IME_CMODE_NATIVE;
  1406. if (bCharSet == SHIFTJIS_CHARSET)
  1407. dwConversion |= IME_CMODE_FULLSHAPE;
  1408. ImmSetConversionStatus(hIMC, dwConversion, dwSentence, _fUsingAIMM);
  1409. }
  1410. }
  1411. ImmReleaseContext(_hwnd, hIMC);
  1412. }
  1413. }
  1414. }
  1415. }