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.

344 lines
10 KiB

  1. //
  2. // compose.cpp
  3. //
  4. // Composition code.
  5. //
  6. #include "globals.h"
  7. #include "mark.h"
  8. #include "editsess.h"
  9. class CCompositionEditSession : public CEditSessionBase
  10. {
  11. public:
  12. CCompositionEditSession(ITfContext *pContext, CMarkTextService *pMark) : CEditSessionBase(pContext)
  13. {
  14. _pMark = pMark;
  15. _pMark->AddRef();
  16. }
  17. ~CCompositionEditSession()
  18. {
  19. _pMark->Release();
  20. }
  21. // ITfEditSession
  22. STDMETHODIMP DoEditSession(TfEditCookie ec);
  23. private:
  24. CMarkTextService *_pMark;
  25. };
  26. class CTerminateCompositionEditSession : public CEditSessionBase
  27. {
  28. public:
  29. CTerminateCompositionEditSession(CMarkTextService *pMark, ITfContext *pContext) : CEditSessionBase(pContext)
  30. {
  31. _pMark = pMark;
  32. _pMark->AddRef();
  33. }
  34. ~CTerminateCompositionEditSession()
  35. {
  36. _pMark->Release();
  37. }
  38. // ITfEditSession
  39. STDMETHODIMP DoEditSession(TfEditCookie ec)
  40. {
  41. _pMark->_TerminateComposition(ec);
  42. return S_OK;
  43. }
  44. private:
  45. CMarkTextService *_pMark;
  46. };
  47. //+---------------------------------------------------------------------------
  48. //
  49. // _TerminateCompositionInContext
  50. //
  51. //----------------------------------------------------------------------------
  52. void CMarkTextService::_TerminateCompositionInContext(ITfContext *pContext)
  53. {
  54. CTerminateCompositionEditSession *pEditSession;
  55. HRESULT hr;
  56. if (pEditSession = new CTerminateCompositionEditSession(this, pContext))
  57. {
  58. pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr);
  59. pEditSession->Release();
  60. }
  61. }
  62. //+---------------------------------------------------------------------------
  63. //
  64. // _Menu_OnComposition
  65. //
  66. // Callback for the "Start/End Composition" menu item.
  67. // If we have a composition, end it. Otherwise start a new composition over
  68. // the selection of the current focus context.
  69. //----------------------------------------------------------------------------
  70. /* static */
  71. void CMarkTextService::_Menu_OnComposition(CMarkTextService *_this)
  72. {
  73. ITfDocumentMgr *pFocusDoc;
  74. ITfContext *pContext;
  75. CCompositionEditSession *pCompositionEditSession;
  76. HRESULT hr;
  77. // get the focus document
  78. if (_this->_pThreadMgr->GetFocus(&pFocusDoc) != S_OK)
  79. return;
  80. // we want the topmost context, since the main doc context could be
  81. // superceded by a modal tip context
  82. if (pFocusDoc->GetTop(&pContext) != S_OK)
  83. {
  84. pContext = NULL;
  85. goto Exit;
  86. }
  87. if (pCompositionEditSession = new CCompositionEditSession(pContext, _this))
  88. {
  89. // we need a document write lock
  90. // the CCompositionEditSession will do all the work when the
  91. // CCompositionEditSession::DoEditSession method is called by the context
  92. pContext->RequestEditSession(_this->_tfClientId, pCompositionEditSession, TF_ES_READWRITE | TF_ES_ASYNCDONTCARE, &hr);
  93. pCompositionEditSession->Release();
  94. }
  95. Exit:
  96. SafeRelease(pContext);
  97. pFocusDoc->Release();
  98. }
  99. //+---------------------------------------------------------------------------
  100. //
  101. // DoEditSession
  102. //
  103. //----------------------------------------------------------------------------
  104. STDAPI CCompositionEditSession::DoEditSession(TfEditCookie ec)
  105. {
  106. ITfInsertAtSelection *pInsertAtSelection;
  107. ITfContextComposition *pContextComposition;
  108. ITfComposition *pComposition;
  109. ITfRange *pRangeComposition;
  110. ITfRange *pRangeInsert;
  111. ITfContext *pCompositionContext;
  112. HRESULT hr;
  113. BOOL fEqualContexts;
  114. // get an interface on the context we can use to deal with compositions
  115. if (_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition) != S_OK)
  116. return E_FAIL;
  117. hr = E_FAIL;
  118. pInsertAtSelection = NULL;
  119. if (_pMark->_IsComposing())
  120. {
  121. // we have a composition, let's terminate it
  122. // it's possible our current composition is in another context...let's find out
  123. fEqualContexts = TRUE;
  124. if (_pMark->_GetComposition()->GetRange(&pRangeComposition) == S_OK)
  125. {
  126. if (pRangeComposition->GetContext(&pCompositionContext) == S_OK)
  127. {
  128. fEqualContexts = IsEqualUnknown(pCompositionContext, _pContext);
  129. if (!fEqualContexts)
  130. {
  131. // need an edit session in the composition context
  132. _pMark->_TerminateCompositionInContext(pCompositionContext);
  133. }
  134. pCompositionContext->Release();
  135. }
  136. pRangeComposition->Release();
  137. }
  138. // if the composition is in pContext, we already have an edit cookie
  139. if (fEqualContexts)
  140. {
  141. _pMark->_TerminateComposition(ec);
  142. }
  143. }
  144. else
  145. {
  146. // let's start a new composition over the current selection
  147. // this is totally contrived, a real text service would have
  148. // some meaningful logic to trigger this
  149. // first, test where a keystroke would go in the document if we did an insert
  150. // we need a special interface to insert text at the selection
  151. if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK)
  152. {
  153. pInsertAtSelection = NULL;
  154. goto Exit;
  155. }
  156. if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert) != S_OK)
  157. goto Exit;
  158. // start the composition
  159. if (pContextComposition->StartComposition(ec, pRangeInsert, _pMark, &pComposition) != S_OK)
  160. {
  161. pComposition = NULL;
  162. }
  163. pRangeInsert->Release();
  164. // _pComposition may be NULL even if StartComposition return S_OK, this mean the application
  165. // rejected the composition
  166. if (pComposition != NULL)
  167. {
  168. _pMark->_SetComposition(pComposition);
  169. // underline the composition text to give the user some feedback UI
  170. _pMark->_SetCompositionDisplayAttributes(ec);
  171. }
  172. }
  173. // if we make it here, we've succeeded
  174. hr = S_OK;
  175. Exit:
  176. SafeRelease(pInsertAtSelection);
  177. pContextComposition->Release();
  178. return hr;
  179. }
  180. //+---------------------------------------------------------------------------
  181. //
  182. // OnCompositionTerminated
  183. //
  184. // Callback for ITfCompositionSink. The system calls this method whenever
  185. // someone other than this service ends a composition.
  186. //----------------------------------------------------------------------------
  187. STDAPI CMarkTextService::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition)
  188. {
  189. // we already have the composition cached, so we can ignore pComposition...
  190. // all this service wants to do is clear the display property
  191. _ClearCompositionDisplayAttributes(ecWrite);
  192. // releae our cached composition
  193. SafeReleaseClear(_pComposition);
  194. return S_OK;
  195. }
  196. //+---------------------------------------------------------------------------
  197. //
  198. // _ClearCompositionDisplayAttributes
  199. //
  200. //----------------------------------------------------------------------------
  201. void CMarkTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec)
  202. {
  203. ITfRange *pRangeComposition;
  204. ITfContext *pContext;
  205. ITfProperty *pDisplayAttributeProperty;
  206. // we need a range and the context it lives in
  207. if (_pComposition->GetRange(&pRangeComposition) != S_OK)
  208. return;
  209. if (pRangeComposition->GetContext(&pContext) != S_OK)
  210. {
  211. pContext = NULL;
  212. goto Exit;
  213. }
  214. // get our the display attribute property
  215. if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
  216. goto Exit;
  217. // clear the value over the range
  218. pDisplayAttributeProperty->Clear(ec, pRangeComposition);
  219. pDisplayAttributeProperty->Release();
  220. Exit:
  221. pRangeComposition->Release();
  222. SafeRelease(pContext);
  223. }
  224. //+---------------------------------------------------------------------------
  225. //
  226. // _SetCompositionDisplayAttributes
  227. //
  228. //----------------------------------------------------------------------------
  229. BOOL CMarkTextService::_SetCompositionDisplayAttributes(TfEditCookie ec)
  230. {
  231. ITfRange *pRangeComposition;
  232. ITfContext *pContext;
  233. ITfProperty *pDisplayAttributeProperty;
  234. VARIANT var;
  235. HRESULT hr;
  236. // we need a range and the context it lives in
  237. if (_pComposition->GetRange(&pRangeComposition) != S_OK)
  238. return FALSE;
  239. hr = E_FAIL;
  240. if (pRangeComposition->GetContext(&pContext) != S_OK)
  241. {
  242. pContext = NULL;
  243. goto Exit;
  244. }
  245. // get our the display attribute property
  246. if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
  247. goto Exit;
  248. // set the value over the range
  249. // the application will use this guid atom to lookup the acutal rendering information
  250. var.vt = VT_I4; // we're going to set a TfGuidAtom
  251. var.lVal = _gaDisplayAttribute; // our cached guid atom for c_guidMarkDisplayAttribute
  252. hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var);
  253. pDisplayAttributeProperty->Release();
  254. Exit:
  255. pRangeComposition->Release();
  256. SafeRelease(pContext);
  257. return (hr == S_OK);
  258. }
  259. //+---------------------------------------------------------------------------
  260. //
  261. // _InitDisplayAttributeGuidAtom
  262. //
  263. // Because it's expensive to map our display attribute GUID to a TSF
  264. // TfGuidAtom, we do it once when Activate is called.
  265. //----------------------------------------------------------------------------
  266. BOOL CMarkTextService::_InitDisplayAttributeGuidAtom()
  267. {
  268. ITfCategoryMgr *pCategoryMgr;
  269. HRESULT hr;
  270. if (CoCreateInstance(CLSID_TF_CategoryMgr,
  271. NULL,
  272. CLSCTX_INPROC_SERVER,
  273. IID_ITfCategoryMgr,
  274. (void**)&pCategoryMgr) != S_OK)
  275. {
  276. return FALSE;
  277. }
  278. hr = pCategoryMgr->RegisterGUID(c_guidMarkDisplayAttribute, &_gaDisplayAttribute);
  279. pCategoryMgr->Release();
  280. return (hr == S_OK);
  281. }