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.

1063 lines
28 KiB

  1. //
  2. // compose.cpp
  3. //
  4. #include "private.h"
  5. #include "compose.h"
  6. #include "ic.h"
  7. #include "range.h"
  8. #include "globals.h"
  9. #include "immxutil.h"
  10. #include "sunka.h"
  11. //////////////////////////////////////////////////////////////////////////////
  12. //
  13. // CEnumCompositionView
  14. //
  15. //////////////////////////////////////////////////////////////////////////////
  16. class CEnumCompositionView : public IEnumITfCompositionView,
  17. public CEnumUnknown,
  18. public CComObjectRootImmx
  19. {
  20. public:
  21. CEnumCompositionView()
  22. {
  23. Dbg_MemSetThisNameID(TEXT("CEnumCompositionView"));
  24. }
  25. BOOL _Init(CComposition *pFirst, CComposition *pHalt);
  26. BEGIN_COM_MAP_IMMX(CEnumCompositionView)
  27. COM_INTERFACE_ENTRY(IEnumITfCompositionView)
  28. END_COM_MAP_IMMX()
  29. IMMX_OBJECT_IUNKNOWN_FOR_ATL()
  30. DECLARE_SUNKA_ENUM(IEnumITfCompositionView, CEnumCompositionView, ITfCompositionView)
  31. private:
  32. DBG_ID_DECLARE;
  33. };
  34. DBG_ID_INSTANCE(CEnumCompositionView);
  35. //+---------------------------------------------------------------------------
  36. //
  37. // _Init
  38. //
  39. //----------------------------------------------------------------------------
  40. BOOL CEnumCompositionView::_Init(CComposition *pFirst, CComposition *pHalt)
  41. {
  42. CComposition *pComposition;
  43. ULONG i;
  44. ULONG cViews;
  45. Assert(pFirst != NULL || pHalt == NULL);
  46. cViews = 0;
  47. // get count
  48. for (pComposition = pFirst; pComposition != pHalt; pComposition = pComposition->_GetNext())
  49. {
  50. cViews++;
  51. }
  52. if ((_prgUnk = SUA_Alloc(cViews)) == NULL)
  53. return FALSE;
  54. _iCur = 0;
  55. _prgUnk->cRef = 1;
  56. _prgUnk->cUnk = cViews;
  57. for (i=0, pComposition = pFirst; pComposition != pHalt; i++, pComposition = pComposition->_GetNext())
  58. {
  59. _prgUnk->rgUnk[i] = (ITfCompositionView *)pComposition;
  60. _prgUnk->rgUnk[i]->AddRef();
  61. }
  62. return TRUE;
  63. }
  64. //////////////////////////////////////////////////////////////////////////////
  65. //
  66. // CComposition
  67. //
  68. //////////////////////////////////////////////////////////////////////////////
  69. /* 3ab2f54c-5357-4759-82c1-bbfe73f44dcc */
  70. const IID IID_PRIV_CCOMPOSITION = { 0x3ab2f54c, 0x5357, 0x4759, {0x82, 0xc1, 0xbb, 0xfe, 0x73, 0xf4, 0x4d, 0xcc} };
  71. inline CComposition *GetCComposition_NA(IUnknown *punk)
  72. {
  73. CComposition *pComposition;
  74. if (punk->QueryInterface(IID_PRIV_CCOMPOSITION, (void **)&pComposition) != S_OK || pComposition == NULL)
  75. return NULL;
  76. pComposition->Release();
  77. return pComposition;
  78. }
  79. DBG_ID_INSTANCE(CComposition);
  80. //+---------------------------------------------------------------------------
  81. //
  82. // _Init
  83. //
  84. //----------------------------------------------------------------------------
  85. BOOL CComposition::_Init(TfClientId tid, CInputContext *pic, IAnchor *paStart, IAnchor *paEnd, ITfCompositionSink *pSink)
  86. {
  87. Assert(_paStart == NULL);
  88. Assert(_paEnd == NULL);
  89. if (paStart->Clone(&_paStart) != S_OK || _paStart == NULL)
  90. {
  91. _paStart = NULL;
  92. goto ExitError;
  93. }
  94. if (paEnd->Clone(&_paEnd) != S_OK || _paEnd == NULL)
  95. {
  96. _paEnd = NULL;
  97. goto ExitError;
  98. }
  99. _tid = tid;
  100. _pic = pic;
  101. _pic->AddRef();
  102. _pSink = pSink;
  103. if (_pSink)
  104. {
  105. _pSink->AddRef();
  106. }
  107. return TRUE;
  108. ExitError:
  109. SafeReleaseClear(_paStart);
  110. SafeReleaseClear(_paEnd);
  111. return FALSE;
  112. }
  113. //+---------------------------------------------------------------------------
  114. //
  115. // _Uninit
  116. //
  117. //----------------------------------------------------------------------------
  118. void CComposition::_Uninit()
  119. {
  120. SafeReleaseClear(_pSink);
  121. SafeReleaseClear(_pic);
  122. SafeReleaseClear(_paStart);
  123. SafeReleaseClear(_paEnd);
  124. }
  125. //+---------------------------------------------------------------------------
  126. //
  127. // GetOwnerClsid
  128. //
  129. //----------------------------------------------------------------------------
  130. STDAPI CComposition::GetOwnerClsid(CLSID *pclsid)
  131. {
  132. if (pclsid == NULL)
  133. return E_INVALIDARG;
  134. if (_IsTerminated())
  135. {
  136. memset(pclsid, 0, sizeof(*pclsid));
  137. return E_UNEXPECTED;
  138. }
  139. return (MyGetGUID(_tid, pclsid) == S_OK ? S_OK : E_FAIL);
  140. }
  141. //+---------------------------------------------------------------------------
  142. //
  143. // GetRange
  144. //
  145. //----------------------------------------------------------------------------
  146. STDAPI CComposition::GetRange(ITfRange **ppRange)
  147. {
  148. CRange *range;
  149. if (ppRange == NULL)
  150. return E_INVALIDARG;
  151. *ppRange = NULL;
  152. if (_IsTerminated())
  153. return E_UNEXPECTED;
  154. if ((range = new CRange) == NULL)
  155. return E_OUTOFMEMORY;
  156. if (!range->_InitWithDefaultGravity(_pic, COPY_ANCHORS, _paStart, _paEnd))
  157. {
  158. range->Release();
  159. return E_FAIL;
  160. }
  161. *ppRange = (ITfRangeAnchor *)range;
  162. return S_OK;
  163. }
  164. //+---------------------------------------------------------------------------
  165. //
  166. // ShiftStart
  167. //
  168. //----------------------------------------------------------------------------
  169. STDAPI CComposition::ShiftStart(TfEditCookie ec, ITfRange *pNewStart)
  170. {
  171. CRange *rangeNewStart;
  172. CRange *range;
  173. IAnchor *paStartNew;
  174. IAnchor *paClearStart;
  175. IAnchor *paClearEnd;
  176. if (_IsTerminated())
  177. return E_UNEXPECTED;
  178. if (!_pic->_IsValidEditCookie(ec, TF_ES_READWRITE))
  179. {
  180. Assert(0);
  181. return TF_E_NOLOCK;
  182. }
  183. if ((rangeNewStart = GetCRange_NA(pNewStart)) == NULL)
  184. return E_INVALIDARG;
  185. if (!VerifySameContext(_pic, rangeNewStart))
  186. return E_INVALIDARG;
  187. paStartNew = rangeNewStart->_GetStart();
  188. if (CompareAnchors(paStartNew, _paStart) <= 0)
  189. {
  190. paClearStart = paStartNew;
  191. paClearEnd = _paStart;
  192. // Set GUID_PROP_COMPOSING
  193. _SetComposing(ec, paClearStart, paClearEnd);
  194. }
  195. else
  196. {
  197. paClearStart = _paStart;
  198. paClearEnd = paStartNew;
  199. // check for crossed anchors
  200. if (CompareAnchors(_paEnd, paStartNew) < 0)
  201. return E_INVALIDARG;
  202. // clear GUID_PROP_COMPOSING
  203. _ClearComposing(ec, paClearStart, paClearEnd);
  204. }
  205. if (_pic->_GetOwnerCompositionSink() != NULL)
  206. {
  207. // notify the app
  208. if (range = new CRange)
  209. {
  210. // make sure the end anchor is positioned correctly
  211. if (range->_InitWithDefaultGravity(_pic, COPY_ANCHORS, paStartNew, _paEnd))
  212. {
  213. _pic->_GetOwnerCompositionSink()->OnUpdateComposition(this, (ITfRangeAnchor *)range);
  214. }
  215. range->Release();
  216. }
  217. }
  218. if (_paStart->ShiftTo(paStartNew) != S_OK)
  219. return E_FAIL;
  220. return S_OK;
  221. }
  222. //+---------------------------------------------------------------------------
  223. //
  224. // ShiftEnd
  225. //
  226. //----------------------------------------------------------------------------
  227. STDAPI CComposition::ShiftEnd(TfEditCookie ec, ITfRange *pNewEnd)
  228. {
  229. CRange *rangeNewEnd;
  230. CRange *range;
  231. IAnchor *paEndNew;
  232. IAnchor *paClearStart;
  233. IAnchor *paClearEnd;
  234. if (_IsTerminated())
  235. return E_UNEXPECTED;
  236. if (!_pic->_IsValidEditCookie(ec, TF_ES_READWRITE))
  237. {
  238. Assert(0);
  239. return TF_E_NOLOCK;
  240. }
  241. if ((rangeNewEnd = GetCRange_NA(pNewEnd)) == NULL)
  242. return E_INVALIDARG;
  243. if (!VerifySameContext(_pic, rangeNewEnd))
  244. return E_INVALIDARG;
  245. paEndNew = rangeNewEnd->_GetEnd();
  246. if (CompareAnchors(paEndNew, _paEnd) >= 0)
  247. {
  248. paClearStart = _paEnd;
  249. paClearEnd = paEndNew;
  250. // Set GUID_PROP_COMPOSING
  251. _SetComposing(ec, paClearStart, paClearEnd);
  252. }
  253. else
  254. {
  255. paClearStart = paEndNew;
  256. paClearEnd = _paEnd;
  257. // check for crossed anchors
  258. if (CompareAnchors(_paStart, paEndNew) > 0)
  259. return E_INVALIDARG;
  260. // clear GUID_PROP_COMPOSING
  261. _ClearComposing(ec, paClearStart, paClearEnd);
  262. }
  263. // notify the app
  264. if (_pic->_GetOwnerCompositionSink() != NULL)
  265. {
  266. if (range = new CRange)
  267. {
  268. // make sure the end anchor is positioned correctly
  269. if (range->_InitWithDefaultGravity(_pic, COPY_ANCHORS, _paStart, paEndNew))
  270. {
  271. _pic->_GetOwnerCompositionSink()->OnUpdateComposition(this, (ITfRangeAnchor *)range);
  272. }
  273. range->Release();
  274. }
  275. }
  276. if (_paEnd->ShiftTo(paEndNew) != S_OK)
  277. return E_FAIL;
  278. return S_OK;
  279. }
  280. //+---------------------------------------------------------------------------
  281. //
  282. // EndComposition
  283. //
  284. // Called by the TIP.
  285. //----------------------------------------------------------------------------
  286. STDAPI CComposition::EndComposition(TfEditCookie ec)
  287. {
  288. if (_IsTerminated())
  289. return E_UNEXPECTED;
  290. if (!_pic->_IsValidEditCookie(ec, TF_ES_READWRITE))
  291. {
  292. Assert(0);
  293. return TF_E_NOLOCK;
  294. }
  295. if (_tid != _pic->_GetClientInEditSession(ec))
  296. {
  297. Assert(0); // caller doesn't own the composition
  298. return E_UNEXPECTED;
  299. }
  300. if (!_pic->_EnterCompositionOp())
  301. return E_UNEXPECTED; // reentrant with another write op
  302. // notify the app
  303. if (_pic->_GetOwnerCompositionSink() != NULL)
  304. {
  305. _pic->_GetOwnerCompositionSink()->OnEndComposition(this);
  306. }
  307. // take this guy off the list of compositions
  308. if (_RemoveFromCompositionList(_pic->_GetCompositionListPtr()))
  309. {
  310. // clear GUID_PROP_COMPOSING
  311. _ClearComposing(ec, _paStart, _paEnd);
  312. }
  313. else
  314. {
  315. Assert(0); // shouldn't get here
  316. }
  317. _pic->_LeaveCompositionOp();
  318. _Uninit();
  319. return S_OK;
  320. }
  321. //+---------------------------------------------------------------------------
  322. //
  323. // _Terminate
  324. //
  325. // Called by Cicero or the app. Caller should already have removed this
  326. // composition from _pCompositionList to catch reentrancy during notifications.
  327. //----------------------------------------------------------------------------
  328. void CComposition::_Terminate(TfEditCookie ec)
  329. {
  330. // notify the tip
  331. _SendOnTerminated(ec, _tid);
  332. // #507778 OnCompositionTerminated() clear _pic by CComposition::_Uninit().
  333. if (_pic)
  334. {
  335. // notify the app
  336. if (_pic->_GetOwnerCompositionSink() != NULL)
  337. {
  338. _pic->_GetOwnerCompositionSink()->OnEndComposition(this);
  339. }
  340. }
  341. // clear GUID_PROP_COMPOSING
  342. _ClearComposing(ec, _paStart, _paEnd);
  343. // kill this composition!
  344. _Uninit();
  345. }
  346. //+---------------------------------------------------------------------------
  347. //
  348. // _SendOnTerminated
  349. //
  350. //----------------------------------------------------------------------------
  351. void CComposition::_SendOnTerminated(TfEditCookie ec, TfClientId tidForEditSession)
  352. {
  353. TfClientId tidTmp;
  354. // _pSink is NULL for default SetText compositions
  355. if (_pSink == NULL)
  356. return;
  357. if (tidForEditSession == _pic->_GetClientInEditSession(ec))
  358. {
  359. // we can skip all the exceptional stuff if all the edits
  360. // will belong to the current lock holder
  361. // this happens when a tip calls StartComposition for a
  362. // second composition and cicero needs to term the first
  363. _pSink->OnCompositionTerminated(ec, this);
  364. }
  365. else
  366. {
  367. // let everyone know about changes so far
  368. // the tip we're about to call may need this info
  369. _pic->_NotifyEndEdit();
  370. // play some games: this is an exceptional case where we may be allowing a
  371. // reentrant edit sess. Need to hack the ec to reflect the composition owner.
  372. tidTmp = _pic->_SetRawClientInEditSession(tidForEditSession);
  373. // notify the tip
  374. _pSink->OnCompositionTerminated(ec, this);
  375. // #507778 OnCompositionTerminated() clear _pic by CComposition::_Uninit().
  376. if (! _pic)
  377. return;
  378. // let everyone know about changes the terminator made
  379. _pic->_NotifyEndEdit();
  380. // put things back the way we found them
  381. _pic->_SetRawClientInEditSession(tidTmp);
  382. }
  383. }
  384. //+---------------------------------------------------------------------------
  385. //
  386. // _AddToCompositionList
  387. //
  388. //----------------------------------------------------------------------------
  389. void CComposition::_AddToCompositionList(CComposition **ppCompositionList)
  390. {
  391. _next = *ppCompositionList;
  392. *ppCompositionList = this;
  393. AddRef();
  394. }
  395. //+---------------------------------------------------------------------------
  396. //
  397. // _RemoveFromCompositionList
  398. //
  399. //----------------------------------------------------------------------------
  400. BOOL CComposition::_RemoveFromCompositionList(CComposition **ppCompositionList)
  401. {
  402. CComposition *pComposition;
  403. // I don't expect many compositions, so this method uses a simple
  404. // scan. We could do something more elaborate for perf if necessary.
  405. while (pComposition = *ppCompositionList)
  406. {
  407. if (pComposition == this)
  408. {
  409. *ppCompositionList = _next;
  410. Release(); // safe because caller already holds ref
  411. return TRUE;
  412. }
  413. ppCompositionList = &pComposition->_next;
  414. }
  415. return FALSE;
  416. }
  417. //+---------------------------------------------------------------------------
  418. //
  419. // _AddToCompositionList
  420. //
  421. //----------------------------------------------------------------------------
  422. /* static */
  423. IRC CComposition::_IsRangeCovered(CInputContext *pic, TfClientId tid,
  424. IAnchor *paStart, IAnchor *paEnd,
  425. CComposition **ppComposition /* not AddRef'd! */)
  426. {
  427. CComposition *pComposition;
  428. IRC irc = IRC_NO_OWNEDCOMPOSITIONS;
  429. *ppComposition = NULL;
  430. for (pComposition = pic->_GetCompositionList(); pComposition != NULL; pComposition = pComposition->_next)
  431. {
  432. if (pComposition->_tid == tid)
  433. {
  434. irc = IRC_OUTSIDE;
  435. if (CompareAnchors(paStart, pComposition->_paStart) >= 0 &&
  436. CompareAnchors(paEnd, pComposition->_paEnd) <= 0)
  437. {
  438. *ppComposition = pComposition;
  439. irc = IRC_COVERED;
  440. break;
  441. }
  442. }
  443. }
  444. return irc;
  445. }
  446. //+---------------------------------------------------------------------------
  447. //
  448. // _ClearComposing
  449. //
  450. //----------------------------------------------------------------------------
  451. void CComposition::_ClearComposing(TfEditCookie ec, IAnchor *paStart, IAnchor *paEnd)
  452. {
  453. CProperty *property;
  454. Assert(!_IsTerminated());
  455. // #507778 OnCompositionTerminated() clear _pic by CComposition::_Uninit().
  456. if (! _pic)
  457. return;
  458. if (_pic->_GetProperty(GUID_PROP_COMPOSING, &property) != S_OK)
  459. return;
  460. property->_ClearInternal(ec, paStart, paEnd);
  461. property->Release();
  462. }
  463. //+---------------------------------------------------------------------------
  464. //
  465. // _SetComposing
  466. //
  467. //----------------------------------------------------------------------------
  468. void CComposition::_SetComposing(TfEditCookie ec, IAnchor *paStart, IAnchor *paEnd)
  469. {
  470. CProperty *property;
  471. if (IsEqualAnchor(paStart, paEnd))
  472. return;
  473. if (_pic->_GetProperty(GUID_PROP_COMPOSING, &property) == S_OK)
  474. {
  475. VARIANT var;
  476. var.vt = VT_I4;
  477. var.lVal = TRUE;
  478. property->_SetDataInternal(ec, paStart, paEnd, &var);
  479. property->Release();
  480. }
  481. }
  482. //////////////////////////////////////////////////////////////////////////////
  483. //
  484. // CInputContext
  485. //
  486. //////////////////////////////////////////////////////////////////////////////
  487. //+---------------------------------------------------------------------------
  488. //
  489. // StartComposition
  490. //
  491. //----------------------------------------------------------------------------
  492. STDAPI CInputContext::StartComposition(TfEditCookie ec, ITfRange *pCompositionRange,
  493. ITfCompositionSink *pSink, ITfComposition **ppComposition)
  494. {
  495. CRange *range;
  496. CComposition *pComposition;
  497. HRESULT hr;
  498. if (ppComposition == NULL)
  499. return E_INVALIDARG;
  500. *ppComposition = NULL;
  501. if (pCompositionRange == NULL || pSink == NULL)
  502. return E_INVALIDARG;
  503. if ((range = GetCRange_NA(pCompositionRange)) == NULL)
  504. return E_INVALIDARG;
  505. if (!VerifySameContext(this, range))
  506. return E_INVALIDARG;
  507. if (!_IsConnected())
  508. return TF_E_DISCONNECTED;
  509. if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
  510. {
  511. Assert(0);
  512. return TF_E_NOLOCK;
  513. }
  514. hr = _StartComposition(ec, range->_GetStart(), range->_GetEnd(), pSink, &pComposition);
  515. *ppComposition = pComposition;
  516. return hr;
  517. }
  518. //+---------------------------------------------------------------------------
  519. //
  520. // _StartComposition
  521. //
  522. // Internal, allow pSink to be NULL, skips verification tests.
  523. //----------------------------------------------------------------------------
  524. HRESULT CInputContext::_StartComposition(TfEditCookie ec, IAnchor *paStart, IAnchor *paEnd,
  525. ITfCompositionSink *pSink, CComposition **ppComposition)
  526. {
  527. BOOL fOk;
  528. CComposition *pComposition;
  529. CComposition *pCompositionRef;
  530. CProperty *property;
  531. VARIANT var;
  532. HRESULT hr;
  533. *ppComposition = NULL;
  534. if (!_EnterCompositionOp())
  535. return E_UNEXPECTED; // reentrant with another write op
  536. hr = S_OK;
  537. if ((pComposition = new CComposition) == NULL)
  538. {
  539. hr = E_OUTOFMEMORY;
  540. goto Exit;
  541. }
  542. if (!pComposition->_Init(_GetClientInEditSession(ec), this, paStart, paEnd, pSink))
  543. {
  544. hr = E_FAIL;
  545. goto Exit;
  546. }
  547. //
  548. // AIMM1.2 expect multiple compositon object. SetText() without creating
  549. // its own composition object should not clear out TIP's composition.
  550. //
  551. // cicero 1.0 -------
  552. // all of our clients only allow a single composition. Let's enforce this behavior
  553. // to protect cicero 1.0 tips in the future.
  554. // kill any existing composition before starting a new one:
  555. // if (_pCompositionList != NULL)
  556. // {
  557. // pCompositionRef = _pCompositionList;
  558. // pCompositionRef->AddRef();
  559. // _TerminateCompositionWithLock(pCompositionRef, ec);
  560. // pCompositionRef->Release();
  561. // Assert(_pCompositionList == NULL);
  562. // }
  563. // cicero 1.0 -------
  564. //
  565. if (_pOwnerComposeSink == NULL) // app may not care about compositions
  566. {
  567. fOk = TRUE;
  568. }
  569. else
  570. {
  571. if (_pOwnerComposeSink->OnStartComposition(pComposition, &fOk) != S_OK)
  572. {
  573. hr = E_FAIL;
  574. goto Exit;
  575. }
  576. if (!fOk)
  577. {
  578. if (_pCompositionList == NULL)
  579. goto Exit; // no current compositions, nothing else to try
  580. // terminate current composition and try again
  581. pCompositionRef = _pCompositionList; // only ref might be in list, so protect the obj
  582. pCompositionRef->AddRef();
  583. _TerminateCompositionWithLock(pCompositionRef, ec);
  584. pCompositionRef->Release();
  585. if (_pOwnerComposeSink->OnStartComposition(pComposition, &fOk) != S_OK)
  586. {
  587. hr = E_FAIL;
  588. goto Exit;
  589. }
  590. if (!fOk)
  591. goto Exit; // we give up
  592. }
  593. }
  594. // set composition property over existing text
  595. if (!IsEqualAnchor(paStart, paEnd) &&
  596. _GetProperty(GUID_PROP_COMPOSING, &property) == S_OK)
  597. {
  598. var.vt = VT_I4;
  599. var.lVal = TRUE;
  600. property->_SetDataInternal(ec, paStart, paEnd, &var);
  601. property->Release();
  602. }
  603. pComposition->_AddToCompositionList(&_pCompositionList);
  604. *ppComposition = pComposition;
  605. Exit:
  606. if (hr != S_OK || !fOk)
  607. {
  608. SafeRelease(pComposition);
  609. }
  610. _LeaveCompositionOp();
  611. return hr;
  612. }
  613. //+---------------------------------------------------------------------------
  614. //
  615. // EnumCompositions
  616. //
  617. //----------------------------------------------------------------------------
  618. STDAPI CInputContext::EnumCompositions(IEnumITfCompositionView **ppEnum)
  619. {
  620. CEnumCompositionView *pEnum;
  621. if (ppEnum == NULL)
  622. return E_INVALIDARG;
  623. *ppEnum = NULL;
  624. if (!_IsConnected())
  625. return TF_E_DISCONNECTED;
  626. if ((pEnum = new CEnumCompositionView) == NULL)
  627. return E_OUTOFMEMORY;
  628. if (!pEnum->_Init(_pCompositionList, NULL))
  629. {
  630. pEnum->Release();
  631. return E_FAIL;
  632. }
  633. *ppEnum = pEnum;
  634. return S_OK;
  635. }
  636. //+---------------------------------------------------------------------------
  637. //
  638. // FindComposition
  639. //
  640. //----------------------------------------------------------------------------
  641. STDAPI CInputContext::FindComposition(TfEditCookie ec, ITfRange *pTestRange,
  642. IEnumITfCompositionView **ppEnum)
  643. {
  644. CComposition *pFirstComp;
  645. CComposition *pHaltComp;
  646. CRange *rangeTest;
  647. CEnumCompositionView *pEnum;
  648. if (ppEnum == NULL)
  649. return E_INVALIDARG;
  650. *ppEnum = NULL;
  651. if (!_IsConnected())
  652. return TF_E_DISCONNECTED;
  653. if (!_IsValidEditCookie(ec, TF_ES_READ))
  654. {
  655. Assert(0);
  656. return TF_E_NOLOCK;
  657. }
  658. if (pTestRange == NULL)
  659. {
  660. return EnumCompositions(ppEnum);
  661. }
  662. if ((rangeTest = GetCRange_NA(pTestRange)) == NULL)
  663. return E_INVALIDARG;
  664. if (!VerifySameContext(this, rangeTest))
  665. return E_INVALIDARG;
  666. // search thru the list, finding anything covered by the range
  667. pFirstComp = NULL;
  668. for (pHaltComp = _pCompositionList; pHaltComp != NULL; pHaltComp = pHaltComp->_GetNext())
  669. {
  670. if (CompareAnchors(rangeTest->_GetEnd(), pHaltComp->_GetStart()) < 0)
  671. break;
  672. if (pFirstComp == NULL)
  673. {
  674. if (CompareAnchors(rangeTest->_GetStart(), pHaltComp->_GetEnd()) <= 0)
  675. {
  676. pFirstComp = pHaltComp;
  677. }
  678. }
  679. }
  680. if (pFirstComp == NULL)
  681. {
  682. // the enum _Init assumes pFirstComp == NULL -> pHaltComp == NULL
  683. pHaltComp = NULL;
  684. }
  685. if ((pEnum = new CEnumCompositionView) == NULL)
  686. return E_OUTOFMEMORY;
  687. if (!pEnum->_Init(pFirstComp, pHaltComp))
  688. {
  689. pEnum->Release();
  690. return E_FAIL;
  691. }
  692. *ppEnum = pEnum;
  693. return S_OK;
  694. }
  695. //+---------------------------------------------------------------------------
  696. //
  697. // TakeOwnership
  698. //
  699. //----------------------------------------------------------------------------
  700. STDAPI CInputContext::TakeOwnership(TfEditCookie ec, ITfCompositionView *pComposition,
  701. ITfCompositionSink *pSink, ITfComposition **ppComposition)
  702. {
  703. if (ppComposition == NULL)
  704. return E_INVALIDARG;
  705. *ppComposition = NULL;
  706. #ifndef UNTESTED_UNUSED
  707. Assert(0); // no one should be calling this
  708. return E_NOTIMPL;
  709. #else
  710. CComposition *composition;
  711. TfClientId tidPrev;
  712. if (pComposition == NULL || pSink == NULL)
  713. return E_INVALIDARG;
  714. if ((composition = GetCComposition_NA(pComposition)) == NULL)
  715. return E_INVALIDARG;
  716. if (composition->_IsTerminated())
  717. return E_INVALIDARG; // it's dead!
  718. if (!_IsConnected())
  719. return TF_E_DISCONNECTED;
  720. if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
  721. {
  722. Assert(0);
  723. return TF_E_NOLOCK;
  724. }
  725. if (!_EnterCompositionOp())
  726. return E_UNEXPECTED; // reentrant with another write op
  727. // switch the owner
  728. tidPrev = composition->_SetOwner(_GetClientInEditSession(ec));
  729. // let the old owner know something happened
  730. composition->_SendOnTerminated(ec, tidPrev);
  731. // switch the sink
  732. composition->_SetSink(pSink);
  733. _LeaveCompositionOp();
  734. return S_OK;
  735. #endif // UNTESTED_UNUSED
  736. }
  737. //+---------------------------------------------------------------------------
  738. //
  739. // TerminateComposition
  740. //
  741. //----------------------------------------------------------------------------
  742. STDAPI CInputContext::TerminateComposition(ITfCompositionView *pComposition)
  743. {
  744. HRESULT hr;
  745. if (!_IsConnected())
  746. return TF_E_DISCONNECTED;
  747. // don't let this happen while we hold a lock
  748. // the usual scenario: word freaks out and tries to cancel the composition inside a SetText call
  749. // let's give them an error code to help debug
  750. if (_IsInEditSession() && _GetTIPOwner() != _tidInEditSession)
  751. {
  752. Assert(0); // someone's trying to abort a composition without a lock, or they don't own the ic
  753. return TF_E_NOLOCK; // meaning the caller doesn't hold the lock
  754. }
  755. if (pComposition == NULL && _pCompositionList == NULL)
  756. return S_OK; // no compositions to terminate, we check later, but check here so we don't fail on read-only docs and for perf
  757. if (!_EnterCompositionOp())
  758. return E_UNEXPECTED; // reentrant with another write op
  759. // need to ask for a lock (call originates with app)
  760. if (_DoPseudoSyncEditSession(TF_ES_READWRITE, PSEUDO_ESCB_TERMCOMPOSITION, pComposition, &hr) != S_OK || hr != S_OK)
  761. {
  762. Assert(0);
  763. hr = E_FAIL;
  764. }
  765. _LeaveCompositionOp();
  766. return hr;
  767. }
  768. //+---------------------------------------------------------------------------
  769. //
  770. // _TerminateCompositionWithLock
  771. //
  772. //----------------------------------------------------------------------------
  773. HRESULT CInputContext::_TerminateCompositionWithLock(ITfCompositionView *pComposition, TfEditCookie ec)
  774. {
  775. CComposition *composition;
  776. Assert(ec != TF_INVALID_EDIT_COOKIE);
  777. if (pComposition == NULL && _pCompositionList == NULL)
  778. return S_OK; // no compositions to terminate
  779. while (TRUE)
  780. {
  781. if (pComposition == NULL)
  782. {
  783. composition = _pCompositionList;
  784. composition->AddRef();
  785. }
  786. else
  787. {
  788. if ((composition = GetCComposition_NA(pComposition)) == NULL)
  789. return E_INVALIDARG;
  790. if (composition->_IsTerminated())
  791. return E_INVALIDARG;
  792. }
  793. composition->_Terminate(ec);
  794. if (!composition->_RemoveFromCompositionList(&_pCompositionList))
  795. {
  796. // how did this guy get off the list w/o termination?
  797. Assert(0); // should never get here
  798. return E_FAIL;
  799. }
  800. if (pComposition != NULL)
  801. break;
  802. composition->Release();
  803. if (_pCompositionList == NULL)
  804. break;
  805. }
  806. return S_OK;
  807. }
  808. //+---------------------------------------------------------------------------
  809. //
  810. // _AbortCompositions
  811. //
  812. // Called on an ic pop. TIPs do not get a notification because we cannot
  813. // guarantee a lock.
  814. //----------------------------------------------------------------------------
  815. void CInputContext::_AbortCompositions()
  816. {
  817. CComposition *pComposition;
  818. while (_pCompositionList != NULL)
  819. {
  820. // notify the app
  821. if (_GetOwnerCompositionSink() != NULL)
  822. {
  823. _GetOwnerCompositionSink()->OnEndComposition(_pCompositionList);
  824. }
  825. // we won't notify the tip because he can't get a lock here
  826. // but there's enough info later to cleanup any state in the ic pop notify
  827. _pCompositionList->_Die();
  828. pComposition = _pCompositionList->_GetNext();
  829. _pCompositionList->Release();
  830. _pCompositionList = pComposition;
  831. }
  832. }