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

2284 lines
72 KiB

  1. //
  2. // correctionimx.cpp
  3. //
  4. #include "private.h"
  5. #ifdef SUPPORT_INTERNAL_WIDGET
  6. #include "globals.h"
  7. #include "timsink.h"
  8. #include "immxutil.h"
  9. #include "sapilayr.h"
  10. #include "spdebug.h"
  11. #include "sphelper.h"
  12. #include "mscandui.h"
  13. #include "computil.h"
  14. #include "ids.h"
  15. #include "cicspres.h"
  16. #include <conio.h>
  17. #define WM_USERLBUTTONDOWN WM_USER+10000 // Replacement for LBUTTONDOWN to fix raid 8828.
  18. LPCTSTR g_lpszClassName = TEXT("CorrectionWidget");
  19. // BUGBUG - Pixel values currently. Need to take account of screen DPI.
  20. const ULONG g_uWidgetWidth = 11;
  21. const ULONG g_uWidgetHeight = 11;
  22. const ULONG g_uActualWidgetHeight = 7;
  23. const ULONG g_uExpandedWidgetWidth = 20;
  24. const ULONG g_uExpandedWidgetHeight = 16;
  25. const ULONG g_uExpandedWidgetXOffset = g_uExpandedWidgetWidth - (g_uExpandedWidgetWidth - g_uWidgetWidth)/2;
  26. const ULONG g_uWidgetYOffset = 0; // Position expanded widget just touching actual edge of correction list when it's above widget.
  27. const ULONG g_uExpandedWidgetYOffset = 0; // Position expanded widget just touching actual edge of correction list when it's above widget.
  28. // ** These NEED to match the icons and each other to avoid misbehavors. **
  29. // Small widget MUST be entirely enclosed by large widget.
  30. const ULONG g_cSloppySelection = 3;
  31. const ULONG g_uTimerLength = 2500; // Time before the widget starts fading out.
  32. const ULONG g_uTimerFade = 10; // Time between alpha decrements for fadeout.
  33. const ULONG g_uAlphaFade = 4; // Alpha fade decrement every 10ms for fadeout effect.
  34. const ULONG g_uAlpha = 216; // Should be multiples of g_uAlphaFade
  35. const ULONG g_uAlphaLarge = 255; // Can be anything.
  36. const ULONG g_uAlphaInvisible = 5; // Needs to be sufficiently non zero that when combined with above, still receives mouse events.
  37. // Can be 4 minimum when combined with alpha 255 for 24/32 bit color mode.
  38. // Needs to be at least 5 when in 16 bit color mode.
  39. const ULONG g_uTimerSloppyMouseLeave = 500; // Time after mouse leave correction window that it resizes small.
  40. /****************************************************************************
  41. * CCorrectionIMX::CCorrectionIMX *
  42. *--------------------------------*
  43. * Description:
  44. * Constructor for Correction 1Tip.
  45. *
  46. * Returns:
  47. * None.
  48. *
  49. * Args:
  50. * None
  51. *
  52. **************************************************************** agarside ***/
  53. CCorrectionIMX::CCorrectionIMX() : m_dwEditCookie(0),
  54. m_dwLayoutCookie(0),
  55. m_dwThreadFocusCookie(0),
  56. m_dwKeyTraceCookie(0),
  57. m_fExpanded(FALSE),
  58. m_hWnd(NULL),
  59. m_hIconInvoke(NULL),
  60. m_hIconInvokeLarge(NULL),
  61. m_hIconInvokeClose(NULL),
  62. m_eWindowState(WINDOW_HIDE),
  63. m_fDisplayAlternatesMyself(FALSE),
  64. m_fCandidateOpen(FALSE),
  65. m_fKeyDown(FALSE),
  66. m_hAtom(0)
  67. {
  68. SPDBG_FUNC("CCorrectionIMX::CCorrectionIMX");
  69. memset(&m_rcSelection, 0, sizeof(m_rcSelection));
  70. }
  71. /****************************************************************************
  72. * CCorrectionIMX::~CCorrectionIMX *
  73. *---------------------------------*
  74. * Description:
  75. * Destructor for Correction Tip
  76. *
  77. * Returns:
  78. * None.
  79. *
  80. **************************************************************** agarside ***/
  81. CCorrectionIMX::~CCorrectionIMX()
  82. {
  83. SPDBG_FUNC("CCorrectionIMX::~CCorrectionIMX");
  84. if (m_hWnd)
  85. {
  86. DestroyWindow(m_hWnd);
  87. }
  88. if (m_hIconInvoke)
  89. {
  90. DestroyIcon(m_hIconInvoke);
  91. m_hIconInvoke = NULL;
  92. }
  93. if (m_hIconInvokeLarge)
  94. {
  95. DestroyIcon(m_hIconInvokeLarge);
  96. m_hIconInvokeLarge = NULL;
  97. }
  98. if (m_hIconInvokeClose)
  99. {
  100. DestroyIcon(m_hIconInvokeClose);
  101. m_hIconInvokeClose = NULL;
  102. }
  103. }
  104. /****************************************************************************
  105. * CCorrectionIMX::FinalConstruct *
  106. *--------------------------------*
  107. * Description:
  108. * Preliminary initialization of the object.
  109. * Creates window class and hidden window for message pump.
  110. * Loads icon resources.
  111. *
  112. * Returns: HRESULT
  113. * S_OK - Everything succeeded.
  114. * Otherwise, appropriate error code.
  115. *
  116. **************************************************************** agarside ***/
  117. HRESULT CCorrectionIMX::FinalConstruct()
  118. {
  119. SPDBG_FUNC("CCorrectionIMX::FinalConstruct");
  120. HRESULT hr = S_OK;
  121. if (SUCCEEDED(hr))
  122. {
  123. m_hIconInvoke = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKE), IMAGE_ICON, g_uWidgetWidth, g_uWidgetHeight, 0);
  124. ASSERT("Failed to create small invocation icon." && m_hIconInvoke);
  125. if (!m_hIconInvoke)
  126. {
  127. hr = SpHrFromLastWin32Error();
  128. }
  129. }
  130. if (SUCCEEDED(hr))
  131. {
  132. m_hIconInvokeLarge = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKE), IMAGE_ICON, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, 0);
  133. ASSERT("Failed to create large invocation icon." && m_hIconInvokeLarge);
  134. if (!m_hIconInvokeLarge)
  135. {
  136. hr = SpHrFromLastWin32Error();
  137. }
  138. }
  139. if (SUCCEEDED(hr))
  140. {
  141. m_hIconInvokeClose = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKECLOSE), IMAGE_ICON, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, 0);
  142. ASSERT("Failed to create large invocation icon." && m_hIconInvokeClose);
  143. if (!m_hIconInvokeClose)
  144. {
  145. hr = SpHrFromLastWin32Error();
  146. }
  147. }
  148. if (SUCCEEDED(hr))
  149. {
  150. if (!CicLoadStringWrapW(g_hInst, IDS_DELETESELECTION, m_wszDelete, ARRAYSIZE(m_wszDelete))
  151. {
  152. hr = E_OUTOFMEMORY;
  153. }
  154. if (!CicLoadStringWrapW(g_hInst, IDS_ADDTODICTIONARYPREFIX, m_wszAddPrefix, ARRAYSIZE(m_wszAddPrefix))
  155. {
  156. hr = E_OUTOFMEMORY;
  157. }
  158. if (!CicLoadStringWrapW(g_hInst, IDS_ADDTODICTIONARYPOSTFIX, m_wszAddPostfix, ARRAYSIZE(m_wszAddPostfix))
  159. {
  160. hr = E_OUTOFMEMORY;
  161. }
  162. }
  163. SPDBG_REPORT_ON_FAIL(hr);
  164. return hr;
  165. }
  166. /****************************************************************************
  167. * CCorrectionIMX::LazyInitializeWindow *
  168. *--------------------------------------*
  169. * Description:
  170. *
  171. * Returns: HRESULT
  172. *
  173. **************************************************************** agarside ***/
  174. HRESULT CCorrectionIMX::LazyInitializeWindow()
  175. {
  176. SPDBG_FUNC("CCorrectionIMX::LazyInitializeWindow");
  177. HRESULT hr = S_OK;
  178. if (m_hWnd)
  179. {
  180. return S_OK;
  181. }
  182. WNDCLASSEX wndClass;
  183. memset(&wndClass, 0, sizeof(wndClass));
  184. wndClass.cbSize = sizeof(wndClass);
  185. wndClass.style = CS_HREDRAW | CS_VREDRAW;
  186. wndClass.lpszClassName = g_lpszClassName;
  187. wndClass.hInstance = _Module.GetModuleInstance();
  188. wndClass.lpfnWndProc = WndProc;
  189. wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  190. ATOM hAtom;
  191. if ((hAtom = RegisterClassEx(&wndClass)) == 0)
  192. {
  193. ASSERT("Failed to register window class." && FALSE);
  194. }
  195. if (SUCCEEDED(hr))
  196. {
  197. m_hAtom = hAtom;
  198. m_hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_LAYERED, g_lpszClassName, g_lpszClassName, WS_POPUP | WS_DISABLED, 0, 0, g_uWidgetWidth, g_uWidgetHeight, NULL, NULL, _Module.GetModuleInstance(), this);
  199. ASSERT("Failed to create hidden window." && m_hWnd);
  200. if (!m_hWnd)
  201. {
  202. hr = SpHrFromLastWin32Error();
  203. }
  204. }
  205. SPDBG_REPORT_ON_FAIL(hr);
  206. return hr;
  207. }
  208. /****************************************************************************
  209. * CCorrectionIMX::DrawWidget *
  210. *----------------------------*
  211. * Description:
  212. *
  213. * Returns: HRESULT
  214. *
  215. * Args:
  216. * BYTE uAlpha
  217. *
  218. **************************************************************** agarside ***/
  219. HRESULT CCorrectionIMX::DrawWidget(BYTE uAlpha)
  220. {
  221. SPDBG_FUNC("CCorrectionIMX::DrawWidget");
  222. HRESULT hr = S_OK;
  223. typedef struct _RGBALPHA {
  224. BYTE rgbBlue;
  225. BYTE rgbGreen;
  226. BYTE rgbRed;
  227. BYTE rgbAlpha;
  228. } RGBALPHA;
  229. HDC hdcScreen = NULL;
  230. HDC hdcLayered = NULL;
  231. RECT rcWindow;
  232. SIZE size;
  233. BITMAPINFO BitmapInfo;
  234. HBITMAP hBitmapMem = NULL;
  235. HBITMAP hBitmapOld = NULL;
  236. void *pDIBits;
  237. int i;
  238. int j;
  239. POINT ptSrc;
  240. POINT ptDst;
  241. BLENDFUNCTION Blend;
  242. BOOL bRet;
  243. RGBALPHA *ppxl;
  244. GetWindowRect( m_hWnd, &rcWindow );
  245. size.cx = rcWindow.right - rcWindow.left;
  246. size.cy = rcWindow.bottom - rcWindow.top;
  247. hdcScreen = GetDC( NULL );
  248. if (hdcScreen == NULL)
  249. {
  250. return E_FAIL;
  251. }
  252. hdcLayered = CreateCompatibleDC( hdcScreen );
  253. if (hdcLayered == NULL)
  254. {
  255. ReleaseDC( NULL, hdcScreen );
  256. return E_FAIL;
  257. }
  258. // create bitmap
  259. BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  260. BitmapInfo.bmiHeader.biWidth = size.cx;
  261. BitmapInfo.bmiHeader.biHeight = size.cy;
  262. BitmapInfo.bmiHeader.biPlanes = 1;
  263. BitmapInfo.bmiHeader.biBitCount = 8 * sizeof(_RGBALPHA);
  264. BitmapInfo.bmiHeader.biCompression = BI_RGB;
  265. BitmapInfo.bmiHeader.biSizeImage = 0;
  266. BitmapInfo.bmiHeader.biXPelsPerMeter = 100;
  267. BitmapInfo.bmiHeader.biYPelsPerMeter = 100;
  268. BitmapInfo.bmiHeader.biClrUsed = 0;
  269. BitmapInfo.bmiHeader.biClrImportant = 0;
  270. hBitmapMem = CreateDIBSection( hdcScreen, &BitmapInfo, DIB_RGB_COLORS, &pDIBits, NULL, 0 );
  271. if (pDIBits == NULL)
  272. {
  273. ReleaseDC( NULL, hdcScreen );
  274. DeleteDC( hdcLayered );
  275. return E_FAIL;
  276. }
  277. ICONINFO iconInfo;
  278. if (m_fExpanded)
  279. {
  280. if (m_eWindowState == WINDOW_LARGE)
  281. {
  282. bRet = GetIconInfo(m_hIconInvokeLarge, &iconInfo);
  283. }
  284. else
  285. {
  286. bRet = GetIconInfo(m_hIconInvokeClose, &iconInfo);
  287. }
  288. }
  289. else
  290. {
  291. bRet = GetIconInfo(m_hIconInvoke, &iconInfo);
  292. }
  293. if (bRet)
  294. {
  295. BITMAP bm, bmMask;
  296. GetObject(iconInfo.hbmColor, sizeof(bm), &bm);
  297. GetObject(iconInfo.hbmMask, sizeof(bmMask), &bmMask);
  298. if (bm.bmPlanes==1 && bmMask.bmBitsPixel==1 && bmMask.bmPlanes==1)
  299. {
  300. ASSERT(bm.bmWidth == size.cx);
  301. ASSERT(bm.bmHeight == size.cy);
  302. // Copy icon into layered window.
  303. GetDIBits(hdcScreen, iconInfo.hbmColor, 0, g_uExpandedWidgetHeight, pDIBits, &BitmapInfo, DIB_RGB_COLORS);
  304. UINT uiNumberBytesMask = bmMask.bmHeight * bmMask.bmWidthBytes;
  305. BYTE *bitmapBytesMask = new BYTE[uiNumberBytesMask];
  306. if (bitmapBytesMask == NULL)
  307. {
  308. hr = E_OUTOFMEMORY;
  309. }
  310. else
  311. {
  312. memset(bitmapBytesMask, 0, uiNumberBytesMask);
  313. int cBytes = GetBitmapBits(iconInfo.hbmMask,uiNumberBytesMask,bitmapBytesMask);
  314. ASSERT(cBytes == uiNumberBytesMask);
  315. if (bRet)
  316. {
  317. for (i = 0; i < size.cy; i++)
  318. {
  319. ppxl = (RGBALPHA *)pDIBits + ((size.cy - i - 1) * size.cx);
  320. for (j = 0; j < size.cx; j++)
  321. {
  322. if ( (bitmapBytesMask[i * bmMask.bmWidthBytes + j/8] >> (7 - (j % 8)))&1)
  323. {
  324. ppxl->rgbRed = 0;
  325. ppxl->rgbBlue = 0;
  326. ppxl->rgbGreen = 0;
  327. ppxl->rgbAlpha = g_uAlphaInvisible;
  328. }
  329. else
  330. {
  331. ppxl->rgbAlpha = 255;
  332. }
  333. ppxl++;
  334. }
  335. }
  336. }
  337. delete [] bitmapBytesMask;
  338. }
  339. }
  340. else
  341. {
  342. ASSERT("Correction icon is an invalid bitmap format." && FALSE);
  343. }
  344. bRet = DeleteObject (iconInfo.hbmColor);
  345. iconInfo.hbmColor=NULL;
  346. ASSERT(bRet);
  347. bRet = DeleteObject (iconInfo.hbmMask);
  348. iconInfo.hbmMask=NULL;
  349. ASSERT(bRet);
  350. }
  351. if (SUCCEEDED(hr))
  352. {
  353. ptSrc.x = 0;
  354. ptSrc.y = 0;
  355. ptDst.x = rcWindow.left;
  356. ptDst.y = rcWindow.top;
  357. Blend.BlendOp = AC_SRC_OVER;
  358. Blend.BlendFlags = 0;
  359. Blend.SourceConstantAlpha = uAlpha;
  360. Blend.AlphaFormat = AC_SRC_ALPHA;
  361. hBitmapOld = (HBITMAP)SelectObject( hdcLayered, hBitmapMem );
  362. bRet = UpdateLayeredWindow(m_hWnd, hdcScreen, &ptDst, &size, hdcLayered, &ptSrc, 0, &Blend, ULW_ALPHA );
  363. if (!bRet)
  364. {
  365. DWORD dw = GetLastError();
  366. }
  367. SelectObject( hdcLayered, hBitmapOld );
  368. }
  369. // done
  370. ReleaseDC( NULL, hdcScreen );
  371. DeleteDC( hdcLayered );
  372. DeleteObject( hBitmapMem );
  373. return hr;
  374. }
  375. /****************************************************************************
  376. * CCorrectionIMX::Activate *
  377. *--------------------------*
  378. * Description:
  379. * Called when Cicero is initialized on a thread.
  380. * Allows us to initialize any Cicero related objects at this point.
  381. *
  382. * Returns: STDAPI
  383. * S_OK - Everything successfully initialized.
  384. * Otherwise, appropriate error code.
  385. *
  386. * Args:
  387. * ITfThreadMgr *ptim
  388. * Pointer to the thread input manager object for the thread.
  389. * TfClientId tid
  390. * Text frameworks client ID for thread.
  391. *
  392. **************************************************************** agarside ***/
  393. STDAPI CCorrectionIMX::Activate(ITfThreadMgr *ptim, TfClientId tid)
  394. {
  395. HRESULT hr = S_OK;
  396. SPDBG_FUNC("CCorrectionIMX::Activate");
  397. ASSERT(m_cptim == NULL);
  398. #if 0
  399. hr = m_cptim.CoCreateInstance(CLSID_TF_ThreadMgr);
  400. // Activate the thread manager
  401. if (S_OK == hr)
  402. {
  403. hr = m_cptim->Activate(&m_tid);
  404. }
  405. #else
  406. m_cptim = ptim;
  407. #endif
  408. m_ptimEventSink = new CThreadMgrEventSink(DIMCallback, ICCallback, this);
  409. if (m_ptimEventSink == NULL)
  410. {
  411. hr = E_OUTOFMEMORY;
  412. }
  413. if (SUCCEEDED(hr))
  414. {
  415. hr = m_ptimEventSink->_Advise(m_cptim);
  416. }
  417. CComPtr<ITfSource> cpSource;
  418. if (SUCCEEDED(hr))
  419. {
  420. hr = m_cptim->QueryInterface(IID_ITfSource, (void **)&cpSource);
  421. }
  422. if (SUCCEEDED(hr))
  423. {
  424. hr = cpSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this, &m_dwThreadFocusCookie);
  425. }
  426. if (SUCCEEDED(hr))
  427. {
  428. hr = cpSource->AdviseSink(IID_ITfKeyTraceEventSink, (ITfKeyTraceEventSink *)this, &m_dwKeyTraceCookie);
  429. }
  430. if (SUCCEEDED(hr))
  431. {
  432. m_ptimEventSink->_InitDIMs(TRUE);
  433. }
  434. CComPtr<ITfFunctionProvider> cpSysFuncPrv;
  435. if (SUCCEEDED(hr))
  436. {
  437. hr = m_cptim->GetFunctionProvider(GUID_SYSTEM_FUNCTIONPROVIDER, &cpSysFuncPrv);
  438. }
  439. if (SUCCEEDED(hr))
  440. {
  441. hr = cpSysFuncPrv->GetFunction(GUID_NULL, IID_ITfFnReconversion, (IUnknown **)&m_cpSysReconv);
  442. }
  443. SPDBG_REPORT_ON_FAIL(hr);
  444. return hr;
  445. }
  446. /****************************************************************************
  447. * CCorrectionIMX::Deactivate *
  448. *----------------------------*
  449. * Description:
  450. * Called when the Cicero thread manager is closing down on a thread.
  451. * Tip is required to deactivate and release all objects.
  452. * Guaranteed to be called if we were ever activated except if a
  453. * catastrophic failure has already occurred.
  454. *
  455. * Returns: STDAPI
  456. * S_OK - Everything succeeded.
  457. * Otherwise, appropriate error code.
  458. *
  459. **************************************************************** agarside ***/
  460. STDAPI CCorrectionIMX::Deactivate()
  461. {
  462. SPDBG_FUNC("CCorrectionIMX::Deactivate");
  463. HRESULT hr = S_OK;
  464. if (m_ptimEventSink)
  465. {
  466. hr = m_ptimEventSink->_InitDIMs(FALSE);
  467. if (SUCCEEDED(hr))
  468. {
  469. hr = m_ptimEventSink->_Unadvise();
  470. }
  471. delete m_ptimEventSink;
  472. m_ptimEventSink = NULL;
  473. }
  474. CComPtr<ITfSource> cpSource;
  475. if (m_cptim && m_cptim->QueryInterface(IID_ITfSource, (void **)&cpSource) == S_OK)
  476. {
  477. cpSource->UnadviseSink(m_dwThreadFocusCookie);
  478. cpSource->UnadviseSink(m_dwKeyTraceCookie);
  479. }
  480. m_cpRangeReconv = NULL;
  481. m_cpRangeUser = NULL;
  482. m_cpRangeWord = NULL;
  483. m_cpSysReconv = NULL;
  484. // m_cptim->Deactivate();
  485. m_cptim.Release();
  486. if (m_hAtom)
  487. {
  488. UnregisterClass((LPCTSTR)m_hAtom, _Module.GetModuleInstance());
  489. }
  490. m_hAtom = NULL;
  491. SPDBG_REPORT_ON_FAIL(hr);
  492. return hr;
  493. }
  494. /****************************************************************************
  495. * CCorrectionIMX::OnSetThreadFocus *
  496. *----------------------------------*
  497. * Description:
  498. * Called by Cicero when our thread gets focus.
  499. * We do nothing here.
  500. *
  501. * Returns: STDAPI
  502. *
  503. * Args:
  504. * void
  505. *
  506. **************************************************************** agarside ***/
  507. STDAPI CCorrectionIMX::OnSetThreadFocus(void)
  508. {
  509. SPDBG_FUNC("CCorrectionIMX::OnSetThreadFocus");
  510. HRESULT hr = S_OK;
  511. // We do nothing here.
  512. SPDBG_REPORT_ON_FAIL(hr);
  513. return hr;
  514. }
  515. /****************************************************************************
  516. * CCorrectionIMX::OnKillThreadFocus *
  517. *-----------------------------------*
  518. * Description:
  519. * Called by Cicero when our thread gets focus.
  520. * We use this to intelligently hide the widget when the app loses focus.
  521. *
  522. * Returns: STDAPI
  523. *
  524. * Args:
  525. * void
  526. *
  527. **************************************************************** agarside ***/
  528. STDAPI CCorrectionIMX::OnKillThreadFocus(void)
  529. {
  530. SPDBG_FUNC("CCorrectionIMX::OnKillThreadFocus");
  531. HRESULT hr = S_OK;
  532. // When we lose focus, we must hide the widget.
  533. hr = Show(WINDOW_HIDE);
  534. SPDBG_REPORT_ON_FAIL(hr);
  535. return hr;
  536. }
  537. /****************************************************************************
  538. * CCorrectionIMX::OnKeyTraceDown *
  539. *--------------------------------*
  540. * Description:
  541. *
  542. * Returns: STDAPI
  543. *
  544. * Args:
  545. * WPARAM wParam
  546. * LPARAM lParam
  547. *
  548. **************************************************************** agarside ***/
  549. STDAPI CCorrectionIMX::OnKeyTraceDown(WPARAM wParam,LPARAM lParam)
  550. {
  551. SPDBG_FUNC("CCorrectionIMX::OnKeyTraceDown");
  552. HRESULT hr = S_OK;
  553. m_fKeyDown = TRUE;
  554. SPDBG_REPORT_ON_FAIL(hr);
  555. return hr;
  556. }
  557. /****************************************************************************
  558. * CCorrectionIMX::OnKeyTraceUp *
  559. *------------------------------*
  560. * Description:
  561. *
  562. * Returns: STDAPI
  563. *
  564. * Args:
  565. * WPARAM wParam
  566. * LPARAM lParam
  567. *
  568. **************************************************************** agarside ***/
  569. STDAPI CCorrectionIMX::OnKeyTraceUp(WPARAM wParam,LPARAM lParam)
  570. {
  571. SPDBG_FUNC("CCorrectionIMX::OnKeyTraceUp");
  572. HRESULT hr = S_OK;
  573. m_fKeyDown = FALSE;
  574. SPDBG_REPORT_ON_FAIL(hr);
  575. return hr;
  576. }
  577. /****************************************************************************
  578. * CCorrectionIMX::OnLayoutChange *
  579. *--------------------------------*
  580. * Description:
  581. * Called by Cicero when the document is resized and/or moved provided Cicero
  582. * application correctly handles this. This allows us to update the location
  583. * of the widget to match the new location of the associated selection.
  584. *
  585. * Returns: STDAPI
  586. * S_OK - Everything succeeded.
  587. * Otherwise, appropriate error code.
  588. *
  589. * Args:
  590. * ITfContext *pic
  591. * Pointer to the input context which was affected.
  592. * TfLayoutCode lcode
  593. * Flag - one of CREATE, CHANGE, DESTROY. We do not currently use this.
  594. * ITfContextView *pView
  595. * Pointer to the context view affected.
  596. *
  597. **************************************************************** agarside ***/
  598. STDAPI CCorrectionIMX::OnLayoutChange(ITfContext *pic, TfLayoutCode lcode, ITfContextView *pView)
  599. {
  600. SPDBG_FUNC("CCorrectionIMX::OnLayoutChange");
  601. HRESULT hr = S_OK;
  602. BOOL fInWriteSession = FALSE;
  603. CEditSession *pes = NULL;
  604. if (m_cpRangeReconv == NULL)
  605. {
  606. return S_OK;
  607. }
  608. // ignore events made by client tip
  609. pic->InWriteSession( m_tid, &fInWriteSession );
  610. if (fInWriteSession)
  611. {
  612. return S_OK;
  613. }
  614. // we only care about the active view
  615. if (!IsActiveView( pic, (ITfContextView *)pView ))
  616. {
  617. return S_OK;
  618. }
  619. pes = new CEditSession( EditSessionCallback );
  620. // move candidate window
  621. if (pes)
  622. {
  623. pes->_state.u = ESCB_RESETTARGETPOS;
  624. pes->_state.pv = this;
  625. pes->_state.wParam = 0;
  626. pes->_state.pRange = NULL;
  627. pes->_state.pic = pic;
  628. pic->RequestEditSession( m_tid, pes, TF_ES_READ | TF_ES_SYNC, &hr );
  629. pes->Release();
  630. }
  631. else
  632. {
  633. hr = E_OUTOFMEMORY;
  634. }
  635. SPDBG_REPORT_ON_FAIL(hr);
  636. return hr;
  637. }
  638. /****************************************************************************
  639. * CCorrectionIMX::IsCandidateObjectOpen *
  640. *---------------------------------------*
  641. * Description:
  642. *
  643. * Returns: HRESULT
  644. * Appropriate error code.
  645. *
  646. * Args:
  647. * BOOL *fOpen
  648. * TRUE if candidate object is open.
  649. *
  650. **************************************************************** agarside ***/
  651. HRESULT CCorrectionIMX::IsCandidateObjectOpen(ITfContext *pic, BOOL *fOpen)
  652. {
  653. SPDBG_FUNC("CCorrectionIMX::IsCandidateObjectOpen");
  654. HRESULT hr = S_OK;
  655. CComPtr<ITfCompartmentMgr> cpCompMgr;
  656. CComPtr<ITfCompartment> cpComp;
  657. CComPtr<ITfContext> cpICTop;
  658. CComPtr<ITfDocumentMgr> cpDim;
  659. CComVariant cpVarCandOpen;
  660. // Default to candidate UI not open in case of failure.
  661. *fOpen = FALSE;
  662. cpVarCandOpen.lVal = 0;
  663. hr = pic->GetDocumentMgr(&cpDim);
  664. if (SUCCEEDED(hr) && cpDim)
  665. {
  666. // Could shortcut check here if cpICTop and pic are the same object (check IUnknowns).
  667. hr = cpDim->GetTop(&cpICTop);
  668. }
  669. if (SUCCEEDED(hr) && cpICTop)
  670. {
  671. hr = cpICTop->QueryInterface(&cpCompMgr);
  672. }
  673. if (SUCCEEDED(hr) && cpCompMgr)
  674. {
  675. hr = cpCompMgr->GetCompartment(GUID_COMPARTMENT_MSCANDIDATEUI_CONTEXT, &cpComp);
  676. }
  677. if (SUCCEEDED(hr) && cpComp)
  678. {
  679. hr = cpComp->GetValue(&cpVarCandOpen);
  680. // If the Top IC has this set to one, then this IC was created by the candidate UI object and hence we
  681. // do *not* want to display the widget.
  682. }
  683. if (SUCCEEDED(hr))
  684. {
  685. *fOpen = (cpVarCandOpen.lVal == 1);
  686. }
  687. SPDBG_REPORT_ON_FAIL(hr);
  688. return hr;
  689. }
  690. /****************************************************************************
  691. * CCorrectionIMX::OnEndEdit *
  692. *---------------------------*
  693. * Description:
  694. * Called when something causes submission of an edit to the input context.
  695. *
  696. * Returns: STDAPI
  697. * S_OK - Everything succeeded.
  698. * Otherwise, appropriate error code.
  699. *
  700. * Args:
  701. * ITfContext *pic
  702. * Input context affected.
  703. * TfEditCookie ecReadOnly
  704. * Read only cookie for immediate use.
  705. * ITfEditRecord *pEditRecord
  706. * Pointer to object allowing the details of the edit to be investigated.
  707. * We use this solely to find out if the selection changed since we do not need
  708. * to take action based on anything else.
  709. *
  710. **************************************************************** agarside ***/
  711. STDAPI CCorrectionIMX::OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord)
  712. {
  713. SPDBG_FUNC("CCorrectionIMX::OnEndEdit");
  714. HRESULT hr = S_OK;
  715. CComPtr<ITfRange> cpRangeUser;
  716. CComPtr<ITfRange> cpRangeWord;
  717. CComPtr<ITfRange> cpRangeReconv;
  718. CComPtr<ITfContextView> cpView;
  719. BOOL fHideWidget = TRUE;
  720. BOOL fSelectionChanged = FALSE;
  721. BOOL fCandOpen = FALSE;
  722. BOOL fHasFocus = TRUE; // If we fail focus check in any way, assume does have focus.
  723. m_fDisplayAlternatesMyself = FALSE;
  724. hr = pic->GetActiveView(&cpView);
  725. if (SUCCEEDED(hr))
  726. {
  727. HWND hWnd;
  728. hr = cpView->GetWnd(&hWnd);
  729. if (hWnd == GetFocus())
  730. {
  731. fHasFocus = TRUE;
  732. }
  733. }
  734. if (fHasFocus && !m_fCandidateOpen)
  735. {
  736. // Candidate list is open. We do not want to display correction widget.
  737. pEditRecord->GetSelectionStatus(&fSelectionChanged);
  738. // if (fSelectionChanged) // This is FALSE when IP first put into RichEdit stage! RE also claims IP is at end of previous utterance :-(.
  739. {
  740. // Get user selection.
  741. BOOL fEmpty = FALSE;
  742. hr = GetSelectionSimple(ecReadOnly, pic, &cpRangeUser);
  743. if (SUCCEEDED(hr))
  744. {
  745. // Check it isn't empty. If it is empty we want to hide the widget.
  746. hr = cpRangeUser->IsEmpty(ecReadOnly, &fEmpty);
  747. }
  748. if (SUCCEEDED(hr) && fEmpty && !m_fKeyDown)
  749. {
  750. BOOL fMatch = FALSE;
  751. if (m_cpRangeUser)
  752. {
  753. cpRangeUser->IsEqualStart(ecReadOnly, m_cpRangeUser, TF_ANCHOR_START, &fMatch);
  754. if (!fMatch)
  755. {
  756. // Check end point.
  757. cpRangeUser->IsEqualStart(ecReadOnly, m_cpRangeUser, TF_ANCHOR_END, &fMatch);
  758. }
  759. }
  760. if (!fMatch)
  761. {
  762. // Find word range (using white space delimiters upto 20 characters either side).
  763. FindWordRange(ecReadOnly, cpRangeUser, &cpRangeWord);
  764. }
  765. }
  766. if (!fEmpty)
  767. {
  768. cpRangeWord = cpRangeUser;
  769. }
  770. if (SUCCEEDED(hr) && cpRangeWord && (!fEmpty || !m_fKeyDown))
  771. {
  772. // Get reconversion range.
  773. BOOL fConvertable = FALSE;
  774. hr = m_cpSysReconv->QueryRange(cpRangeWord, &cpRangeReconv, &fConvertable);
  775. // Will validly fail if there is no alternates - e.g. partial words or typed text.
  776. hr = S_OK;
  777. BOOL fMatch = FALSE;
  778. if (SUCCEEDED(hr) && fConvertable)
  779. {
  780. hr = DoesUserSelectionMatchReconversion(ecReadOnly, cpRangeWord, cpRangeReconv, &fMatch);
  781. // May not be convertable or ranges may not match.
  782. }
  783. if (SUCCEEDED(hr) && fMatch)
  784. {
  785. fHideWidget = FALSE;
  786. // Convertable and ranges do match.
  787. }
  788. else
  789. {
  790. if (fEmpty)
  791. {
  792. cpRangeReconv = NULL;
  793. cpRangeReconv = cpRangeWord;
  794. fHideWidget = FALSE;
  795. m_fDisplayAlternatesMyself = TRUE;
  796. }
  797. else
  798. {
  799. // Find word range (using white space delimiters upto 20 characters either side).
  800. CComPtr<ITfRange> cpRangeWordTmp;
  801. CComPtr<ITfRange> cpRangeClone;
  802. hr = cpRangeWord->Clone(&cpRangeClone);
  803. if (SUCCEEDED(hr))
  804. {
  805. hr = cpRangeClone->Collapse(ecReadOnly, TF_ANCHOR_START);
  806. }
  807. if (SUCCEEDED(hr))
  808. {
  809. LONG pcch = 0;
  810. hr = cpRangeClone->ShiftStart(ecReadOnly, 1, &pcch, NULL);
  811. }
  812. if (SUCCEEDED(hr))
  813. {
  814. hr = FindWordRange(ecReadOnly, cpRangeClone, &cpRangeWordTmp);
  815. }
  816. if (cpRangeWordTmp)
  817. {
  818. fMatch = FALSE;
  819. hr = DoesUserSelectionMatchReconversion(ecReadOnly, cpRangeWord, cpRangeWordTmp, &fMatch);
  820. }
  821. if (SUCCEEDED(hr) && cpRangeWordTmp && fMatch)
  822. {
  823. cpRangeReconv = NULL;
  824. cpRangeReconv = cpRangeWord;
  825. fHideWidget = FALSE;
  826. m_fDisplayAlternatesMyself = TRUE;
  827. }
  828. }
  829. }
  830. }
  831. }
  832. }
  833. if (m_fCandidateOpen || !fHasFocus)
  834. {
  835. SPDBG_REPORT_ON_FAIL(hr);
  836. return hr;
  837. }
  838. if (fHideWidget)
  839. {
  840. m_cpRangeReconv = NULL;
  841. if (cpRangeUser)
  842. {
  843. m_cpRangeUser = NULL;
  844. m_cpRangeUser = cpRangeUser;
  845. }
  846. if (cpRangeWord)
  847. {
  848. m_cpRangeWord = NULL;
  849. m_cpRangeWord = cpRangeWord;
  850. }
  851. Show(WINDOW_HIDE);
  852. }
  853. else
  854. {
  855. m_cpRangeUser = NULL;
  856. m_cpRangeUser = cpRangeUser;
  857. m_cpRangeWord = NULL;
  858. m_cpRangeWord = cpRangeWord;
  859. m_cpRangeReconv = NULL;
  860. m_cpRangeReconv = cpRangeReconv;
  861. m_cpic = pic;
  862. // Update selection screen coordinates to match user selection (not reconversion range).
  863. hr = UpdateWidgetLocation(ecReadOnly);
  864. if (SUCCEEDED(hr))
  865. {
  866. Show(WINDOW_SMALLSHOW);
  867. }
  868. }
  869. SPDBG_REPORT_ON_FAIL(hr);
  870. return hr;
  871. }
  872. // PRIVATE FUNCTIONS
  873. /****************************************************************************
  874. * CCorrectionIMX::CompareRange *
  875. *------------------------------*
  876. * Description:
  877. * Compare two ranges and set a boolean value TRUE if they match.
  878. *
  879. * Returns: HRESULT
  880. * S_OK - Everything succeeded.
  881. * Otherwise, appropriate error code.
  882. *
  883. * Args:
  884. * TfEditCookie ecReadOnly
  885. * Edit cookie.
  886. * ITfRange *pRange1
  887. * First range.
  888. * ITfRange *pRange2
  889. * Second range.
  890. * BOOL *fIdentical
  891. * Boolean return value. TRUE = ranges match.
  892. *
  893. **************************************************************** agarside ***/
  894. HRESULT CCorrectionIMX::CompareRange(TfEditCookie ecReadOnly, ITfRange *pRange1, ITfRange *pRange2, BOOL *fIdentical)
  895. {
  896. SPDBG_FUNC("CCorrectionIMX::CompareRange");
  897. HRESULT hr = S_OK;
  898. LONG lStartResult = -1, lEndResult = -1;
  899. *fIdentical = FALSE;
  900. hr = pRange1->CompareStart(ecReadOnly, pRange2, TF_ANCHOR_START, &lStartResult);
  901. if (SUCCEEDED(hr))
  902. {
  903. hr = pRange1->CompareEnd(ecReadOnly, pRange2, TF_ANCHOR_END, &lEndResult);
  904. }
  905. if (SUCCEEDED(hr) && lStartResult == 0 && lEndResult == 0)
  906. {
  907. *fIdentical = TRUE;
  908. }
  909. SPDBG_REPORT_ON_FAIL(hr);
  910. return hr;
  911. }
  912. /****************************************************************************
  913. * CCorrectionIMX::FindWordRange *
  914. *-------------------------------*
  915. * Description:
  916. *
  917. * Returns: HRESULT
  918. * S_OK - Everything succeeded.
  919. * Otherwise, appropriate error code.
  920. *
  921. * Args:
  922. * TfEditCookie ecReadOnly
  923. * Edit cookie.
  924. * ITfRange *pRangeIP
  925. * Range of the IP (zero length)
  926. * ITfRange *ppRangeWord
  927. * Returned range of the word found by simple word breaking algorithm.
  928. *
  929. **************************************************************** agarside ***/
  930. HRESULT CCorrectionIMX::FindWordRange(TfEditCookie ecReadOnly, ITfRange *pRangeIP, ITfRange **ppRangeWord)
  931. {
  932. SPDBG_FUNC("CCorectionIMX::FindWordRange");
  933. HRESULT hr = S_OK;
  934. CComPtr<ITfRangeACP> cpRangeIPACP;
  935. LONG cchStart = 0, cchEnd = 0, iStart = 0, iEnd = 0;
  936. ULONG cchTotal = 0;
  937. WCHAR wzText[41];
  938. *ppRangeWord = NULL;
  939. if (SUCCEEDED(hr))
  940. {
  941. hr = pRangeIP->QueryInterface(IID_ITfRangeACP, (void **)&cpRangeIPACP);
  942. }
  943. if (SUCCEEDED(hr))
  944. {
  945. hr = cpRangeIPACP->ShiftStart(ecReadOnly, -20, &cchStart, NULL);
  946. }
  947. if (SUCCEEDED(hr))
  948. {
  949. hr = cpRangeIPACP->ShiftEnd(ecReadOnly, 20, &cchEnd, NULL);
  950. }
  951. if (SUCCEEDED(hr))
  952. {
  953. hr = cpRangeIPACP->GetText(ecReadOnly, 0, wzText, 40, &cchTotal);
  954. }
  955. wzText[cchTotal] = 0;
  956. if (SUCCEEDED(hr))
  957. {
  958. iStart = abs(cchStart);
  959. while (iStart >= 0)
  960. {
  961. iStart --;
  962. if (wzText[iStart] < 'A' ||
  963. wzText[iStart] > 'z' ||
  964. (wzText[iStart] > 'Z' && wzText[iStart] < 'a') )
  965. {
  966. break;
  967. }
  968. }
  969. iStart ++;
  970. if (iStart == abs(cchStart))
  971. {
  972. // Special case - do not show widget on IP when IP at start of the word.
  973. return S_OK;
  974. }
  975. iEnd = abs(cchStart);
  976. while (iEnd < (LONG)cchTotal)
  977. {
  978. if (wzText[iEnd] < 'A' ||
  979. wzText[iEnd] > 'z' ||
  980. (wzText[iEnd] > 'Z' && wzText[iEnd] < 'a') )
  981. {
  982. break;
  983. }
  984. iEnd ++;
  985. }
  986. if (iEnd == abs(cchStart))
  987. {
  988. return S_OK;
  989. }
  990. LONG cchTemp;
  991. if (iStart > 0)
  992. {
  993. hr = cpRangeIPACP->ShiftStart(ecReadOnly, iStart, &cchTemp, NULL);
  994. }
  995. if (SUCCEEDED(hr))
  996. {
  997. if (iEnd < (LONG)cchTotal)
  998. {
  999. hr = cpRangeIPACP->ShiftEnd(ecReadOnly, iEnd - cchTotal, &cchTemp, NULL);
  1000. }
  1001. }
  1002. if (SUCCEEDED(hr))
  1003. {
  1004. hr = pRangeIP->Clone(ppRangeWord);
  1005. }
  1006. }
  1007. SPDBG_REPORT_ON_FAIL(hr);
  1008. return hr;
  1009. }
  1010. /****************************************************************************
  1011. * CCorrectionIMX::DoesUserSelectionMatchReconversion *
  1012. *----------------------------------------------------*
  1013. * Description:
  1014. * Compares the user selection to the reconversion range returned by the tip.
  1015. * Need to match up exactly barring a small amount of white space at start and end.
  1016. *
  1017. * Returns: HRESULT
  1018. * S_OK - Everything succeeded.
  1019. * Otherwise, appropriate error code.
  1020. *
  1021. * Args:
  1022. * TfEditCookie ecReadOnly
  1023. * Edit cookie.
  1024. * ITfRange *pRangeUser
  1025. * User selection range.
  1026. * BOOL *fMatch
  1027. * Boolean return value for whether they match or not.
  1028. *
  1029. **************************************************************** agarside ***/
  1030. HRESULT CCorrectionIMX::DoesUserSelectionMatchReconversion(TfEditCookie ecReadOnly, ITfRange *pRangeUser, ITfRange *pRangeReconv, BOOL *fMatch)
  1031. {
  1032. SPDBG_FUNC("CCorrectionIMX::DoesUserSelectionMatchReconversion");
  1033. HRESULT hr = S_OK;
  1034. CComPtr<ITfRangeACP> cpRangeUserACP, cpReconvACP;
  1035. CComPtr<ITfRange> cpRangeClone;
  1036. *fMatch = FALSE;
  1037. hr = pRangeUser->QueryInterface(IID_ITfRangeACP, (void **)&cpRangeUserACP);
  1038. if (SUCCEEDED(hr))
  1039. {
  1040. hr = pRangeReconv->QueryInterface(IID_ITfRangeACP, (void **)&cpReconvACP);
  1041. }
  1042. if (cpRangeUserACP && cpReconvACP)
  1043. {
  1044. while (TRUE)
  1045. {
  1046. LONG iStartSelection, iStartReconv, iEndSelection, iEndReconv;
  1047. ULONG startchars, endchars;
  1048. LONG cch;
  1049. WCHAR starttext[g_cSloppySelection+1], endtext[g_cSloppySelection+1];
  1050. // Get start indexs and end offsets.
  1051. cpRangeUserACP->GetExtent(&iStartSelection, &iEndSelection);
  1052. cpReconvACP->GetExtent(&iStartReconv, &iEndReconv);
  1053. // Convert end character positions to absolute values.
  1054. iEndSelection += iStartSelection;
  1055. iEndReconv += iStartReconv;
  1056. if (abs(iStartSelection-iStartReconv) > g_cSloppySelection ||
  1057. abs(iEndSelection-iEndReconv) > g_cSloppySelection)
  1058. {
  1059. // Two much of a mismatch between selection and reconversion range.
  1060. // Do not display widget.
  1061. break;
  1062. }
  1063. if (abs(iStartSelection-iStartReconv) == 0 &&
  1064. abs(iEndSelection - iEndReconv) == 0)
  1065. {
  1066. // Shortcut check.
  1067. *fMatch = TRUE;
  1068. break;
  1069. }
  1070. if (iStartSelection<iStartReconv)
  1071. {
  1072. hr = cpRangeUserACP->GetText(ecReadOnly, 0, starttext, abs(iStartSelection-iStartReconv), &startchars);
  1073. }
  1074. else
  1075. {
  1076. hr = cpReconvACP->GetText(ecReadOnly, 0, starttext, abs(iStartSelection-iStartReconv), &startchars);
  1077. }
  1078. starttext[startchars] = 0;
  1079. if (SUCCEEDED(hr))
  1080. {
  1081. if (iEndSelection<iEndReconv)
  1082. {
  1083. hr = pRangeReconv->Clone(&cpRangeClone);
  1084. if (SUCCEEDED(hr))
  1085. {
  1086. hr = cpRangeClone->ShiftStart(ecReadOnly, iEndSelection-iStartReconv, &cch, NULL);
  1087. }
  1088. }
  1089. else
  1090. {
  1091. hr = pRangeUser->Clone(&cpRangeClone);
  1092. if (SUCCEEDED(hr))
  1093. {
  1094. hr = cpRangeClone->ShiftStart(ecReadOnly, iEndReconv-iStartSelection, &cch, NULL);
  1095. }
  1096. }
  1097. }
  1098. if (SUCCEEDED(hr))
  1099. {
  1100. hr = cpRangeClone->GetText(ecReadOnly, 0, endtext, abs(iEndSelection-iEndReconv), &endchars);
  1101. endtext[endchars] = 0;
  1102. UINT i;
  1103. if (iStartSelection != iStartReconv)
  1104. {
  1105. for (i = 0; i < (UINT)abs(iStartReconv-iStartSelection); i++)
  1106. {
  1107. if (starttext[i] != L' ')
  1108. {
  1109. break;
  1110. }
  1111. }
  1112. if (i != (UINT)abs(iStartReconv-iStartSelection))
  1113. {
  1114. break;
  1115. }
  1116. }
  1117. if (iEndSelection != iEndReconv)
  1118. {
  1119. for (i = 0; i < (UINT)abs(iEndReconv-iEndSelection); i++)
  1120. {
  1121. if (endtext[i] != L' ' && endtext[i] != 13)
  1122. {
  1123. // 13 is found when selection hits end of richedit contents.
  1124. // Range is 1 longer than it should be and contains a carriage return.
  1125. break;
  1126. }
  1127. }
  1128. if (i != (UINT)abs(iEndReconv-iEndSelection))
  1129. {
  1130. break;
  1131. }
  1132. }
  1133. *fMatch = TRUE;
  1134. break;
  1135. }
  1136. // We failed. We need to exit the loop now.
  1137. break;
  1138. }
  1139. }
  1140. SPDBG_REPORT_ON_FAIL(hr);
  1141. return hr;
  1142. }
  1143. /****************************************************************************
  1144. * CCorrectionIMX::GetReconversion *
  1145. *---------------------------------*
  1146. * Description:
  1147. *
  1148. * Returns: HRESULT
  1149. *
  1150. * Args:
  1151. * TfEditCookie ec
  1152. * ITfCandidateList** ppCandList
  1153. *
  1154. **************************************************************** agarside ***/
  1155. HRESULT CCorrectionIMX::GetReconversion(TfEditCookie ec, ITfCandidateList** ppCandList)
  1156. {
  1157. SPDBG_FUNC("CCorrectionIMX::GetReconversion");
  1158. HRESULT hr = E_NOTIMPL;
  1159. SPDBG_REPORT_ON_FAIL(hr);
  1160. return hr;
  1161. }
  1162. /****************************************************************************
  1163. * CCorrectionIMX::Show *
  1164. *----------------------*
  1165. * Description:
  1166. * Shows / hides / and resizes / repositions correction widget window as
  1167. * requested.
  1168. *
  1169. * Returns: HRESULT
  1170. * S_OK - Everything succeeded.
  1171. * Otherwise, appropriate error code.
  1172. *
  1173. * Args:
  1174. * WINDOWSTATE eWindowState
  1175. * One of 5 enumerations stating requested action.
  1176. *
  1177. **************************************************************** agarside ***/
  1178. HRESULT CCorrectionIMX::Show(WINDOWSTATE eWindowState)
  1179. {
  1180. SPDBG_FUNC("CCorrectionIMX::Show");
  1181. HRESULT hr = S_OK;
  1182. m_eWindowState = eWindowState;
  1183. // First update the expanded state flag.
  1184. switch (m_eWindowState)
  1185. {
  1186. case WINDOW_HIDE:
  1187. case WINDOW_SMALL:
  1188. case WINDOW_SMALLSHOW:
  1189. {
  1190. m_fExpanded = FALSE;
  1191. break;
  1192. }
  1193. case WINDOW_LARGE:
  1194. case WINDOW_LARGECLOSE:
  1195. {
  1196. m_fExpanded = TRUE;
  1197. break;
  1198. }
  1199. case WINDOW_REFRESH:
  1200. {
  1201. break;
  1202. }
  1203. default:
  1204. {
  1205. ASSERT("Reached default in CCorrectionIMX::Show" && FALSE);
  1206. }
  1207. }
  1208. // Need to ensure we delete hRgn3 in all cases below where we do not pass it to SetWindowRgn.
  1209. UINT uTop = m_rcSelection.top + g_uWidgetYOffset; //( m_rcSelection.top + m_rcSelection.bottom - g_uWidgetHeight ) / 2;
  1210. UINT uTopExpanded = m_rcSelection.top + g_uExpandedWidgetYOffset; //( m_rcSelection.top + m_rcSelection.bottom - g_uExpandedWidgetHeight ) / 2;
  1211. if (SUCCEEDED(hr))
  1212. {
  1213. // Now show/hide the window as requested.
  1214. switch (m_eWindowState)
  1215. {
  1216. case WINDOW_HIDE:
  1217. {
  1218. KillTimer(m_hWnd, ID_HIDETIMER);
  1219. KillTimer(m_hWnd, ID_FADETIMER);
  1220. ShowWindow(m_hWnd, SW_HIDE);
  1221. break;
  1222. }
  1223. case WINDOW_SMALLSHOW:
  1224. {
  1225. if (!m_hWnd)
  1226. {
  1227. LazyInitializeWindow();
  1228. }
  1229. KillTimer(m_hWnd, ID_FADETIMER);
  1230. SetTimer(m_hWnd, ID_HIDETIMER, g_uTimerLength, NULL);
  1231. // Should be same as WINDOW_SMALL
  1232. SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, g_uWidgetWidth, g_uWidgetHeight, SWP_NOACTIVATE);
  1233. // Now show the window in new location.
  1234. ShowWindow(m_hWnd, SW_SHOWNA);
  1235. DrawWidget(g_uAlpha);
  1236. break;
  1237. }
  1238. case WINDOW_SMALL:
  1239. {
  1240. KillTimer(m_hWnd, ID_FADETIMER);
  1241. SetTimer(m_hWnd, ID_HIDETIMER, g_uTimerLength, NULL);
  1242. // Resize and reposition window.
  1243. SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, g_uWidgetWidth, g_uWidgetHeight, SWP_NOACTIVATE);
  1244. // Now we can switch to TRUE layered window drawing.
  1245. DrawWidget(g_uAlpha);
  1246. break;
  1247. }
  1248. case WINDOW_LARGE:
  1249. case WINDOW_LARGECLOSE:
  1250. {
  1251. KillTimer(m_hWnd, ID_FADETIMER);
  1252. KillTimer(m_hWnd, ID_HIDETIMER);
  1253. // Resize and reposition window.
  1254. SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uExpandedWidgetWidth, uTopExpanded, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, SWP_NOACTIVATE);
  1255. DrawWidget(g_uAlphaLarge);
  1256. break;
  1257. }
  1258. case WINDOW_REFRESH:
  1259. {
  1260. KillTimer(m_hWnd, ID_FADETIMER);
  1261. // Do not kill hide timer here - the window has moved is all that is happening.
  1262. if (m_fExpanded)
  1263. {
  1264. SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uExpandedWidgetWidth, uTopExpanded, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  1265. }
  1266. else
  1267. {
  1268. SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
  1269. }
  1270. break;
  1271. }
  1272. default:
  1273. {
  1274. ASSERT("Reached default in CCorrectionIMX::Show" && FALSE);
  1275. break;
  1276. }
  1277. }
  1278. }
  1279. SPDBG_REPORT_ON_FAIL(hr);
  1280. return hr;
  1281. }
  1282. /****************************************************************************
  1283. * CCorrectionIMX::WndProc *
  1284. *-------------------------*
  1285. * Description:
  1286. * Windows message handling procedure for hidden window.
  1287. *
  1288. * Returns: LRESULT CALLBACK
  1289. * Appropriate return values based on Windows message received.
  1290. *
  1291. * Args:
  1292. * HWND hWnd
  1293. * Handle to hidden window.
  1294. * UINT uMsg
  1295. * Message number.
  1296. * WPARAM wParam
  1297. * Message data.
  1298. * LPARAM lParam
  1299. * Message data.
  1300. *
  1301. **************************************************************** agarside ***/
  1302. /* static */
  1303. LRESULT CALLBACK CCorrectionIMX::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1304. {
  1305. SPDBG_FUNC("CCorrectionIMX::WndProc");
  1306. static CCorrectionIMX *_this = NULL;
  1307. switch (uMsg)
  1308. {
  1309. case WM_TIMER:
  1310. {
  1311. if (wParam == ID_HIDETIMER)
  1312. {
  1313. KillTimer(_this->m_hWnd, ID_HIDETIMER);
  1314. _this->m_uAlpha = g_uAlpha;
  1315. SetTimer(_this->m_hWnd, ID_FADETIMER, g_uTimerFade, NULL);
  1316. }
  1317. if (wParam == ID_FADETIMER)
  1318. {
  1319. _this->m_uAlpha -= g_uAlphaFade;
  1320. if (_this->m_uAlpha <= 0)
  1321. {
  1322. KillTimer(_this->m_hWnd, ID_FADETIMER);
  1323. _this->Show(WINDOW_HIDE);
  1324. }
  1325. else
  1326. {
  1327. _this->DrawWidget((BYTE)_this->m_uAlpha);
  1328. }
  1329. }
  1330. if (wParam == ID_MOUSELEAVETIMER)
  1331. {
  1332. POINT pt;
  1333. GetCursorPos(&pt);
  1334. ScreenToClient(hWnd, &pt);
  1335. LPARAM lP = MAKELPARAM(pt.x, pt.y);
  1336. PostMessage(hWnd, WM_MOUSEMOVE, 0, lP);
  1337. }
  1338. break;
  1339. }
  1340. case WM_SETCURSOR:
  1341. {
  1342. if (HIWORD(lParam) == WM_LBUTTONDOWN)
  1343. {
  1344. PostMessage(hWnd, WM_USERLBUTTONDOWN, 0, 0);
  1345. return 1;
  1346. }
  1347. if (HIWORD(lParam) == WM_MOUSEMOVE)
  1348. {
  1349. POINT pt;
  1350. GetCursorPos(&pt);
  1351. ScreenToClient(hWnd, &pt);
  1352. LPARAM lP = MAKELPARAM(pt.x, pt.y);
  1353. PostMessage(hWnd, WM_MOUSEMOVE, 0, lP);
  1354. }
  1355. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1356. return 1;
  1357. }
  1358. case WM_NCCREATE:
  1359. {
  1360. _this = (CCorrectionIMX *)(((CREATESTRUCT *)lParam)->lpCreateParams);
  1361. return 1;
  1362. }
  1363. case WM_MOUSEACTIVATE:
  1364. {
  1365. // We are interested in the mouse click but don't want to activate.
  1366. return MA_NOACTIVATE;
  1367. }
  1368. case WM_MOUSEMOVE:
  1369. {
  1370. ::KillTimer(hWnd, ID_MOUSELEAVETIMER);
  1371. int xPos = LOWORD(lParam);
  1372. int yPos = HIWORD(lParam);
  1373. if (!_this->m_fExpanded)
  1374. {
  1375. if (xPos > 0 && yPos > 0 && xPos < g_uWidgetWidth && yPos < g_uWidgetHeight)
  1376. {
  1377. _this->Show(WINDOW_LARGE);
  1378. ::SetTimer(hWnd, ID_MOUSELEAVETIMER, g_uTimerSloppyMouseLeave, NULL);
  1379. }
  1380. }
  1381. else
  1382. {
  1383. if (xPos < 0 || yPos < 0 || xPos >= g_uExpandedWidgetWidth || yPos >= g_uExpandedWidgetHeight)
  1384. {
  1385. if (_this->m_eWindowState != WINDOW_LARGECLOSE)
  1386. {
  1387. _this->Show(WINDOW_SMALL);
  1388. }
  1389. }
  1390. else
  1391. {
  1392. ::SetTimer(hWnd, ID_MOUSELEAVETIMER, g_uTimerSloppyMouseLeave, NULL);
  1393. }
  1394. }
  1395. return 1;
  1396. }
  1397. case WM_USERLBUTTONDOWN:
  1398. {
  1399. HRESULT hr = S_OK;
  1400. CEditSession *pes;
  1401. // Is it within our window if we are the small widget?
  1402. if (_this->m_fExpanded)
  1403. {
  1404. // Need to call this first, but it will reset the m_fExpanded flag.
  1405. if (_this->m_eWindowState == WINDOW_LARGE)
  1406. {
  1407. _this->Show(WINDOW_LARGECLOSE);
  1408. if (_this->m_cpRangeReconv)
  1409. {
  1410. if (!_this->m_fDisplayAlternatesMyself)
  1411. {
  1412. hr = _this->m_cpSysReconv->Reconvert(_this->m_cpRangeReconv);
  1413. }
  1414. else
  1415. {
  1416. pes = new CEditSession( EditSessionCallback );
  1417. if (pes)
  1418. {
  1419. pes->_state.u = ESCB_RECONVERTMYSELF;
  1420. pes->_state.pv = _this;
  1421. pes->_state.wParam = 0;
  1422. pes->_state.pRange = _this->m_cpRangeReconv;
  1423. pes->_state.pic = _this->GetIC();
  1424. pes->_state.pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
  1425. pes->Release();
  1426. }
  1427. }
  1428. }
  1429. }
  1430. else
  1431. {
  1432. // Send escape character which by design, causes candidate UI to close :-).
  1433. INPUT escape[2];
  1434. escape[0].type = INPUT_KEYBOARD;
  1435. escape[0].ki.wVk = 0;
  1436. escape[0].ki.wScan = 27;
  1437. escape[0].ki.dwFlags = KEYEVENTF_UNICODE;
  1438. escape[1] = escape[0];
  1439. escape[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
  1440. SendInput(2, escape, sizeof(escape[0]));
  1441. }
  1442. }
  1443. else
  1444. {
  1445. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1446. }
  1447. return 1;
  1448. }
  1449. }
  1450. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1451. }
  1452. /****************************************************************************
  1453. * CCorrectionIMX::UpdateWidgetLocation *
  1454. *--------------------------------------*
  1455. * Description:
  1456. * Queries Cicero for the location of the selected text in order to
  1457. * position the widget appropriately. The selection is adjusted when the
  1458. * last the right edge of the document and the selection ends just to the
  1459. * left of the next word on the next line of the document. In this case,
  1460. * we want to display the widget at the right side of the document on the
  1461. * first line so we remove all trailing spaces to find this position.
  1462. *
  1463. * Returns: HRESULT
  1464. * S_OK - Everything succeeded.
  1465. * Otherwise, appropriate error code.
  1466. *
  1467. * Args:
  1468. * void
  1469. *
  1470. **************************************************************** agarside ***/
  1471. HRESULT CCorrectionIMX::UpdateWidgetLocation(TfEditCookie ec)
  1472. {
  1473. SPDBG_FUNC("CCorrectionIMX::UpdateWidgetLocation");
  1474. HRESULT hr = S_OK;
  1475. if (m_cpRangeWord)
  1476. {
  1477. CComPtr<ITfRange> cpCollapsedRange;
  1478. hr = m_cpRangeWord->Clone(&cpCollapsedRange);
  1479. if (SUCCEEDED(hr))
  1480. {
  1481. hr = cpCollapsedRange->Collapse(ec, TF_ANCHOR_START);
  1482. }
  1483. BOOL fClipped = FALSE;
  1484. if (SUCCEEDED(hr))
  1485. {
  1486. hr = GetTextExtInActiveView(ec, cpCollapsedRange, &m_rcSelection, &fClipped);
  1487. }
  1488. }
  1489. SPDBG_REPORT_ON_FAIL(hr);
  1490. return hr;
  1491. }
  1492. /****************************************************************************
  1493. * CCorrectionIMX::EditSessionCallback *
  1494. *-------------------------------------*
  1495. * Description:
  1496. * Called by Cicero when a request for an edit session has been granted.
  1497. *
  1498. * Returns: HRESULT
  1499. * S_OK - Everything succeeded.
  1500. * Otherwise, appropriate error code.
  1501. *
  1502. * Args:
  1503. * TfEditCookie ec
  1504. * Appropriate edit cookie as requested.
  1505. * CEditSession *pes
  1506. * Object containing data and pointers supplied when edit session was requested.
  1507. *
  1508. **************************************************************** agarside ***/
  1509. /* static */
  1510. HRESULT CCorrectionIMX::EditSessionCallback(TfEditCookie ec, CEditSession *pes)
  1511. {
  1512. SPDBG_FUNC("CCorrectionIMX::EditSessionCallback");
  1513. HRESULT hr = S_OK;
  1514. CCorrectionIMX *_this = (CCorrectionIMX *)pes->_state.pv;
  1515. switch (pes->_state.u)
  1516. {
  1517. case ESCB_RESETTARGETPOS:
  1518. {
  1519. if (_this->m_cpRangeWord)
  1520. {
  1521. // Find new location of selection (range)
  1522. hr = _this->UpdateWidgetLocation(ec);
  1523. if (SUCCEEDED(hr))
  1524. {
  1525. // Reposition correction widget without resizing.
  1526. hr = _this->Show(WINDOW_REFRESH);
  1527. }
  1528. }
  1529. break;
  1530. }
  1531. case ESCB_RECONVERTMYSELF:
  1532. {
  1533. CComPtr<ITfCandidateList> cpCandList;
  1534. WCHAR wzText[MAX_PATH];
  1535. WCHAR wzWord[MAX_PATH];
  1536. WCHAR *wzTmp;
  1537. ULONG cchTotal;
  1538. LANGID langid = GetUserDefaultLangID(); // BUGBUG - How should we get this value?
  1539. CCandidateList *pCandList = new CCandidateList(CCorrectionIMX::SetResult, pes->_state.pic, pes->_state.pRange, CCorrectionIMX::SetOptionResult);
  1540. if (NULL == pCandList)
  1541. {
  1542. hr = E_OUTOFMEMORY;
  1543. }
  1544. if (SUCCEEDED(hr))
  1545. {
  1546. hr = SetSelectionSimple(ec, pes->_state.pic, pes->_state.pRange);
  1547. }
  1548. if (SUCCEEDED(hr))
  1549. {
  1550. cchTotal = 0;
  1551. hr = pes->_state.pRange->GetText(ec, 0, wzText, ARRAYSIZE(wzText)-1, &cchTotal);
  1552. wzText[cchTotal] = 0;
  1553. // Space strip word without altering contents of wzText as we use this later on.
  1554. wzTmp = wzText;
  1555. while (*wzTmp == ' ')
  1556. {
  1557. wzTmp++;
  1558. cchTotal--;
  1559. }
  1560. wcscpy(wzWord, wzTmp);
  1561. while (cchTotal > 1 && wzWord[cchTotal-1] == ' ')
  1562. {
  1563. cchTotal--;
  1564. wzWord[cchTotal] = 0;
  1565. }
  1566. }
  1567. if (SUCCEEDED(hr))
  1568. {
  1569. if (cchTotal > 0)
  1570. {
  1571. // Toggle case of first letter.
  1572. WCHAR wzTmp[2];
  1573. wzTmp[0] = wzText[0];
  1574. wzTmp[1] = 0;
  1575. _wcslwr(wzTmp);
  1576. if (wzTmp[0] == wzText[0])
  1577. {
  1578. _wcsupr(wzTmp);
  1579. }
  1580. if (SUCCEEDED(hr) && wzText[0] != wzTmp[0])
  1581. {
  1582. wzText[0] = wzTmp[0];
  1583. hr = pCandList->AddString(wzText, langid, (void **)_this, NULL, NULL, 0);
  1584. }
  1585. }
  1586. }
  1587. if (SUCCEEDED(hr))
  1588. {
  1589. HRESULT tmpHr = _this->IsWordInDictionary(wzWord);
  1590. if ( tmpHr == S_FALSE )
  1591. {
  1592. CComBSTR cpbstr;
  1593. cpbstr.Append(_this->m_wszAddPrefix);
  1594. cpbstr.Append(wzWord);
  1595. cpbstr.Append(_this->m_wszAddPostfix);
  1596. hr = pCandList->AddOption(cpbstr, langid, (void **)_this, NULL, NULL, 0, NULL, wzWord);
  1597. }
  1598. }
  1599. if (SUCCEEDED(hr))
  1600. {
  1601. HICON hDeleteIcon = LoadIcon(GetSpgrmrInstance(), MAKEINTRESOURCE(IDI_SPTIP_DELETEICON));
  1602. hr = pCandList->AddOption(_this->m_wszDelete, langid, (void **)_this, NULL, NULL, 2, hDeleteIcon, NULL);
  1603. }
  1604. if (SUCCEEDED(hr))
  1605. {
  1606. hr = pCandList->QueryInterface(IID_ITfCandidateList, (void **)&cpCandList);
  1607. }
  1608. if (SUCCEEDED(hr))
  1609. {
  1610. hr = _this->ShowCandidateList(ec, pes->_state.pic, pes->_state.pRange, cpCandList);
  1611. }
  1612. if (pCandList)
  1613. {
  1614. pCandList->Release();
  1615. }
  1616. break;
  1617. }
  1618. case ESCB_INJECTALTERNATETEXT:
  1619. {
  1620. CComPtr<ITfRange> cpInsertionPoint;
  1621. hr = GetSelectionSimple(ec, pes->_state.pic, &cpInsertionPoint);
  1622. cpInsertionPoint->SetText(ec, 0, (BSTR)pes->_state.wParam, -1);
  1623. cpInsertionPoint->Collapse(ec, TF_ANCHOR_END);
  1624. SetSelectionSimple(ec, pes->_state.pic, cpInsertionPoint);
  1625. SysFreeString((BSTR)pes->_state.wParam);
  1626. break;
  1627. }
  1628. }
  1629. SPDBG_REPORT_ON_FAIL(hr);
  1630. return hr;
  1631. }
  1632. /****************************************************************************
  1633. * CCorrectionIMX::IsWordInDictionary *
  1634. *------------------------------------*
  1635. * Description:
  1636. *
  1637. * Returns: HRESULT
  1638. *
  1639. * Args:
  1640. * WCHAR *pwzWord
  1641. *
  1642. **************************************************************** agarside ***/
  1643. HRESULT CCorrectionIMX::IsWordInDictionary(WCHAR *pwzWord)
  1644. {
  1645. SPDBG_FUNC("CCorrectionIMX::IsWordInDictionary");
  1646. HRESULT hr = S_OK;
  1647. // here we should query the speech user dictionary.
  1648. if (!m_cpLexicon)
  1649. {
  1650. hr = m_cpLexicon.CoCreateInstance(CLSID_SpLexicon);
  1651. }
  1652. if (SUCCEEDED(hr) && m_cpLexicon)
  1653. {
  1654. SPWORDPRONUNCIATIONLIST spwordpronlist;
  1655. memset(&spwordpronlist, 0, sizeof(spwordpronlist));
  1656. // Find out status of word in lexicon.
  1657. hr = m_cpLexicon->GetPronunciations(pwzWord, 0x409, eLEXTYPE_USER | eLEXTYPE_APP, &spwordpronlist);
  1658. if (hr == SPERR_NOT_IN_LEX)
  1659. {
  1660. hr = S_FALSE;
  1661. }
  1662. else if (hr == SP_WORD_EXISTS_WITHOUT_PRONUNCIATION)
  1663. {
  1664. hr = S_OK;
  1665. }
  1666. //free all the buffers
  1667. CoTaskMemFree(spwordpronlist.pvBuffer);
  1668. }
  1669. SPDBG_REPORT_ON_FAIL(hr);
  1670. return hr;
  1671. }
  1672. /****************************************************************************
  1673. * CCorrectionIMX::AddWordToDictionary *
  1674. *-------------------------------------*
  1675. * Description:
  1676. *
  1677. * Returns: HRESULT
  1678. *
  1679. * Args:
  1680. * WCHAR *pwzWord
  1681. *
  1682. **************************************************************** agarside ***/
  1683. HRESULT CCorrectionIMX::AddWordToDictionary(WCHAR *pwzWord)
  1684. {
  1685. SPDBG_FUNC("CCorrectionIMX::AddWordToDictionary");
  1686. HRESULT hr = S_OK;
  1687. if (m_cpLexicon)
  1688. {
  1689. // Add in unknown pronciation.
  1690. hr = m_cpLexicon->AddPronunciation(pwzWord, 0x409, SPPS_Unknown, NULL);
  1691. }
  1692. SPDBG_REPORT_ON_FAIL(hr);
  1693. return hr;
  1694. }
  1695. /****************************************************************************
  1696. * CCorrectionIMX::RemoveWordFromDictionary *
  1697. *------------------------------------------*
  1698. * Description:
  1699. *
  1700. * Returns: HRESULT
  1701. *
  1702. * Args:
  1703. * WCHAR *pwzWord
  1704. *
  1705. **************************************************************** agarside ***/
  1706. HRESULT CCorrectionIMX::RemoveWordFromDictionary(WCHAR *pwzWord)
  1707. {
  1708. SPDBG_FUNC("CCorrectionIMX::RemoveWordFromDictionary");
  1709. HRESULT hr = S_OK;
  1710. if (m_cpLexicon)
  1711. {
  1712. // Since the last paremeter is NULL here, all instances of the word are removed.
  1713. hr = m_cpLexicon->RemovePronunciation(pwzWord, 0x409, SPPS_Unknown, NULL);
  1714. // IGNORE ERRORS DELIBERATELY HERE.
  1715. ASSERT("Unexpected error trying to clear word in user lexicon." && (SUCCEEDED(hr) || hr == SPERR_NOT_IN_LEX));
  1716. hr = S_OK;
  1717. }
  1718. SPDBG_REPORT_ON_FAIL(hr);
  1719. return hr;
  1720. }
  1721. /****************************************************************************
  1722. * CCorrectionIMX::ShowCandidateList *
  1723. *-----------------------------------*
  1724. * Description:
  1725. *
  1726. * Returns: HRESULT
  1727. *
  1728. * Args:
  1729. * TfEditCookie ec
  1730. * ITfContext *pic
  1731. * ITfRange *pRange
  1732. * ITfCandidateList *pCandList
  1733. *
  1734. **************************************************************** agarside ***/
  1735. HRESULT CCorrectionIMX::ShowCandidateList(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfCandidateList *pCandList)
  1736. {
  1737. SPDBG_FUNC("CCorrectionIMX::ShowCandidateList");
  1738. HRESULT hr = S_OK;
  1739. CComPtr<ITfDocumentMgr> cpdim;
  1740. // Create candidate object if necessary. Use previously created object if we have it.
  1741. if (m_cpCandUIEx == NULL)
  1742. {
  1743. hr = m_cpCandUIEx.CoCreateInstance(CLSID_TFCandidateUI);
  1744. }
  1745. if (SUCCEEDED(hr))
  1746. {
  1747. hr = pic->GetDocumentMgr(&cpdim);
  1748. }
  1749. if (SUCCEEDED(hr))
  1750. {
  1751. m_cpCandUIEx->SetClientId(GetId());
  1752. }
  1753. if (SUCCEEDED(hr))
  1754. {
  1755. hr = m_cpCandUIEx->SetCandidateList(pCandList);
  1756. }
  1757. if (SUCCEEDED(hr))
  1758. {
  1759. hr = m_cpCandUIEx->OpenCandidateUI(NULL, cpdim, ec, pRange);
  1760. }
  1761. return S_OK;
  1762. }
  1763. /****************************************************************************
  1764. * CCorrectionIMX::SetResult *
  1765. *---------------------------*
  1766. * Description:
  1767. *
  1768. * Returns: HRESULT
  1769. *
  1770. * Args:
  1771. * ITfContext *pic
  1772. * ITfRange *pRange
  1773. * CCandidateString *pCand
  1774. * TfCandidateResult imcr
  1775. *
  1776. **************************************************************** agarside ***/
  1777. HRESULT CCorrectionIMX::SetResult(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
  1778. {
  1779. SPDBG_FUNC("CCorrectionIMX::SetResult");
  1780. BSTR bstr;
  1781. CCorrectionIMX *_this = (CCorrectionIMX *)pCand->_pv;
  1782. HRESULT hr = S_OK;
  1783. CEditSession *pes = NULL;
  1784. if (imcr == CAND_FINALIZED)
  1785. {
  1786. ULONG ulID = 0;
  1787. pCand->GetID(&ulID);
  1788. switch (ulID)
  1789. {
  1790. case 0: // Reverse capitalized choice.
  1791. {
  1792. pCand->GetString(&bstr);
  1793. pes = new CEditSession( EditSessionCallback );
  1794. if (pes)
  1795. {
  1796. pes->_state.u = ESCB_INJECTALTERNATETEXT;
  1797. pes->_state.pv = _this;
  1798. pes->_state.wParam = (WPARAM)bstr;
  1799. pes->_state.pRange = pRange;
  1800. pes->_state.pic = pic;
  1801. pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
  1802. pes->Release();
  1803. hr = S_OK;
  1804. // Do not return errors from this function or Cicero will disable the tip.
  1805. }
  1806. // Do not call SysFreeString - called inside edit session.
  1807. break;
  1808. }
  1809. // BUGBUG - Need to handle words starting with non-alphabetic characters better.
  1810. }
  1811. }
  1812. // close candidate UI if it's still there
  1813. if (imcr == CAND_FINALIZED || imcr == CAND_CANCELED)
  1814. {
  1815. if (_this->m_cpCandUIEx)
  1816. {
  1817. _this->m_cpCandUIEx->CloseCandidateUI();
  1818. }
  1819. }
  1820. SPDBG_REPORT_ON_FAIL(hr);
  1821. return hr;
  1822. }
  1823. /****************************************************************************
  1824. * CCorrectionIMX::SetOptionResult *
  1825. *---------------------------------*
  1826. * Description:
  1827. *
  1828. * Returns: HRESULT
  1829. *
  1830. * Args:
  1831. * ITfContext *pic
  1832. * ITfRange *pRange
  1833. * CCandidateString *pCand
  1834. * TfCandidateResult imcr
  1835. *
  1836. **************************************************************** agarside ***/
  1837. HRESULT CCorrectionIMX::SetOptionResult(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
  1838. {
  1839. SPDBG_FUNC("CCorrectionIMX::SetOptionResult");
  1840. BSTR bstr;
  1841. CCorrectionIMX *_this = (CCorrectionIMX *)pCand->_pv;
  1842. HRESULT hr = S_OK;
  1843. CEditSession *pes = NULL;
  1844. if (imcr == CAND_FINALIZED)
  1845. {
  1846. ULONG ulID = 0;
  1847. pCand->GetID(&ulID);
  1848. switch (ulID)
  1849. {
  1850. case 0: // Add to dictinary...
  1851. {
  1852. if (SUCCEEDED(pCand->GetWord(&bstr)))
  1853. {
  1854. _this->AddWordToDictionary(bstr);
  1855. SysFreeString(bstr);
  1856. }
  1857. break;
  1858. }
  1859. #ifdef ENABLEDELETE
  1860. case 1: // Delete from dictinary...
  1861. {
  1862. if (SUCCEEDED(pCand->GetWord(&bstr)))
  1863. {
  1864. _this->RemoveWordFromDictionary(bstr);
  1865. SysFreeString(bstr);
  1866. }
  1867. break;
  1868. #endif
  1869. case 2: // Delete
  1870. {
  1871. bstr = SysAllocString(L"");
  1872. pes = new CEditSession( EditSessionCallback );
  1873. if (pes)
  1874. {
  1875. pes->_state.u = ESCB_INJECTALTERNATETEXT;
  1876. pes->_state.pv = _this;
  1877. pes->_state.wParam = (WPARAM)bstr;
  1878. pes->_state.pRange = pRange;
  1879. pes->_state.pic = pic;
  1880. pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
  1881. pes->Release();
  1882. hr = S_OK;
  1883. }
  1884. break;
  1885. }
  1886. }
  1887. }
  1888. // close candidate UI if it's still there
  1889. if (imcr == CAND_FINALIZED || imcr == CAND_CANCELED)
  1890. {
  1891. if (_this->m_cpCandUIEx)
  1892. {
  1893. _this->m_cpCandUIEx->CloseCandidateUI();
  1894. }
  1895. }
  1896. SPDBG_REPORT_ON_FAIL(hr);
  1897. return hr;
  1898. }
  1899. /****************************************************************************
  1900. * CCorrectionIMX::ICCallback *
  1901. *----------------------------*
  1902. * Description:
  1903. * Called by Cicero when changes happen to the DIM with focus.
  1904. * We need to listen to this so we can hide the widget on a focus change within an app.
  1905. * (e.g. two instances of Microsoft Word windows)
  1906. *
  1907. * Returns: HRESULT
  1908. * S_OK - Everything succeeded.
  1909. * Otherwise, appropriate error code.
  1910. *
  1911. * Args:
  1912. * UINT uCode
  1913. * Code specifying change to IC.
  1914. * ITfDicumentMgr *pdim
  1915. * ITfDicumentMgr *pdimPrevFocus
  1916. * void *pv
  1917. *
  1918. **************************************************************** agarside ***/
  1919. /* static */
  1920. HRESULT CCorrectionIMX::DIMCallback(UINT uCode, ITfDocumentMgr *pdim, ITfDocumentMgr *pdimPrevFocus, void *pv)
  1921. {
  1922. SPDBG_FUNC("CCorrectionIMX::DIMCallback");
  1923. HRESULT hr = S_OK;
  1924. CCorrectionIMX *_this = (CCorrectionIMX *)pv;
  1925. CComPtr<ITfSource> cpSource;
  1926. switch (uCode)
  1927. {
  1928. case TIM_CODE_SETFOCUS:
  1929. {
  1930. hr = _this->Show(WINDOW_HIDE);
  1931. break;
  1932. }
  1933. }
  1934. SPDBG_REPORT_ON_FAIL(hr);
  1935. return hr;
  1936. }
  1937. /****************************************************************************
  1938. * CCorrectionIMX::ICCallback *
  1939. *----------------------------*
  1940. * Description:
  1941. * Called by Cicero when changes happen to an input context.
  1942. * We need to listen to this so we can hook up to edit and layout changes.
  1943. *
  1944. * Returns: HRESULT
  1945. * S_OK - Everything succeeded.
  1946. * Otherwise, appropriate error code.
  1947. *
  1948. * Args:
  1949. * UINT uCode
  1950. * Code specifying change to IC.
  1951. * ITfContext *pic
  1952. * Interface for the affected IC.
  1953. * void *pv
  1954. * Pointer for change - specific data.
  1955. * We don't use this.
  1956. *
  1957. **************************************************************** agarside ***/
  1958. /* static */
  1959. HRESULT CCorrectionIMX::ICCallback(UINT uCode, ITfContext *pic, void *pv)
  1960. {
  1961. SPDBG_FUNC("CCorrectionIMX::ICCallback");
  1962. HRESULT hr = S_OK;
  1963. CCorrectionIMX *_this = (CCorrectionIMX *)pv;
  1964. CComPtr<ITfSource> cpSource;
  1965. CICPriv *priv = NULL;
  1966. switch (uCode)
  1967. {
  1968. case TIM_CODE_INITIC:
  1969. {
  1970. if ((priv = GetInputContextPriv(_this->GetId(), pic)) == NULL)
  1971. {
  1972. break;
  1973. }
  1974. _this->InitICPriv(_this->GetId(), priv, pic);
  1975. _this->IsCandidateObjectOpen(pic, &_this->m_fCandidateOpen);
  1976. if (_this->m_fCandidateOpen)
  1977. {
  1978. _this->Show(WINDOW_LARGECLOSE);
  1979. }
  1980. break;
  1981. }
  1982. case TIM_CODE_UNINITIC:
  1983. {
  1984. if ((priv = GetInputContextPriv(_this->GetId(), pic)) == NULL)
  1985. {
  1986. break;
  1987. }
  1988. _this->DeleteICPriv(priv, pic);
  1989. BOOL fOpen;
  1990. _this->IsCandidateObjectOpen(pic, &fOpen);
  1991. if (fOpen)
  1992. {
  1993. _this->m_fCandidateOpen = FALSE;
  1994. // If widget is in LARGECLOSE state, must close it here.
  1995. _this->Show(WINDOW_HIDE);
  1996. }
  1997. break;
  1998. }
  1999. }
  2000. SPDBG_REPORT_ON_FAIL(hr);
  2001. return hr;
  2002. }
  2003. /****************************************************************************
  2004. * CCorrectionIMX::InitICPriv *
  2005. *----------------------------*
  2006. * Description:
  2007. *
  2008. * Returns: HRESULT
  2009. *
  2010. * Args:
  2011. * TfClientId tid
  2012. * CICPriv *priv
  2013. * ITfContext *pic
  2014. *
  2015. **************************************************************** agarside ***/
  2016. HRESULT CCorrectionIMX::InitICPriv(TfClientId tid, CICPriv *priv, ITfContext *pic)
  2017. {
  2018. CComPtr<ITfSource> cpSource;
  2019. HRESULT hr = S_OK;
  2020. priv->_tid = tid;
  2021. priv->_pic = pic; // not AddRef'd, this struct is contained in life of the pic
  2022. hr = pic->QueryInterface(IID_ITfSource, (void **)&cpSource);
  2023. if (cpSource)
  2024. {
  2025. hr = cpSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &priv->m_dwEditCookie);
  2026. if (SUCCEEDED(hr))
  2027. {
  2028. hr = cpSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &priv->m_dwLayoutCookie);
  2029. }
  2030. }
  2031. SPDBG_REPORT_ON_FAIL(hr);
  2032. return hr;
  2033. }
  2034. /****************************************************************************
  2035. * CCorrectionIMX::DeleteICPriv *
  2036. *------------------------------*
  2037. * Description:
  2038. *
  2039. * Returns: HRESULT
  2040. *
  2041. * Args:
  2042. * CICPriv *picp
  2043. * ITfContext *pic
  2044. *
  2045. **************************************************************** agarside ***/
  2046. HRESULT CCorrectionIMX::DeleteICPriv(CICPriv *picp, ITfContext *pic)
  2047. {
  2048. CComPtr<ITfSource> cpSource;
  2049. HRESULT hr = S_OK;
  2050. if (!picp)
  2051. {
  2052. return E_FAIL;
  2053. }
  2054. hr = pic->QueryInterface(IID_ITfSource, (void **)&cpSource);
  2055. if (SUCCEEDED(hr))
  2056. {
  2057. cpSource->UnadviseSink(picp->m_dwEditCookie);
  2058. cpSource->UnadviseSink(picp->m_dwLayoutCookie);
  2059. }
  2060. // we MUST clear out the private data before cicero is free
  2061. // to release the ic
  2062. ClearCompartment(GetId(), pic, GUID_IC_PRIVATE, FALSE);
  2063. SPDBG_REPORT_ON_FAIL(hr);
  2064. return hr;
  2065. }
  2066. //+---------------------------------------------------------------------------
  2067. //
  2068. // CreateInstance
  2069. //
  2070. // This is our internal object creator. We only call this method
  2071. // when creating a wrapper for a specific tip, or when an app
  2072. // uses TF_CreateThreadMgr. Never from CoCreateInstance.
  2073. //----------------------------------------------------------------------------
  2074. /* static */
  2075. HRESULT
  2076. CCorrectionIMX::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)
  2077. {
  2078. return _CreatorClass::CreateInstance(pUnkOuter, riid, ppvObj);
  2079. }
  2080. #endif // SUPPORT_INTERNAL_WIDGET