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.

1580 lines
46 KiB

  1. //
  2. // tom.cpp
  3. //
  4. #include "private.h"
  5. #include "range.h"
  6. #include "tim.h"
  7. #include "ic.h"
  8. #include "immxutil.h"
  9. #include "dim.h"
  10. #include "view.h"
  11. #include "tsi.h"
  12. #include "compose.h"
  13. #include "profiles.h"
  14. #include "fnrecon.h"
  15. #include "acp2anch.h"
  16. //+---------------------------------------------------------------------------
  17. //
  18. // GetSelection
  19. //
  20. //----------------------------------------------------------------------------
  21. STDAPI CInputContext::GetSelection(TfEditCookie ec, ULONG ulIndex, ULONG ulCount, TF_SELECTION *pSelection, ULONG *pcFetched)
  22. {
  23. HRESULT hr;
  24. TS_SELECTION_ANCHOR sel;
  25. TS_SELECTION_ANCHOR *pSelAnchor;
  26. ULONG i;
  27. ULONG j;
  28. CRange *range;
  29. if (pcFetched == NULL)
  30. return E_INVALIDARG;
  31. *pcFetched = 0;
  32. if (pSelection == NULL && ulCount > 0)
  33. return E_INVALIDARG;
  34. if (!_IsConnected())
  35. return TF_E_DISCONNECTED;
  36. if (!_IsValidEditCookie(ec, TF_ES_READ))
  37. {
  38. Assert(0);
  39. return TF_E_NOLOCK;
  40. }
  41. if (ulCount == 1)
  42. {
  43. pSelAnchor = &sel;
  44. }
  45. else if ((pSelAnchor = (TS_SELECTION_ANCHOR *)cicMemAlloc(ulCount*sizeof(TS_SELECTION_ANCHOR))) == NULL)
  46. return E_OUTOFMEMORY;
  47. if ((hr = _ptsi->GetSelection(ulIndex, ulCount, pSelAnchor, pcFetched)) != S_OK)
  48. goto Exit;
  49. // verify the anchors
  50. for (i=0; i<*pcFetched; i++)
  51. {
  52. if (pSelAnchor[i].paStart == NULL ||
  53. pSelAnchor[i].paEnd == NULL ||
  54. CompareAnchors(pSelAnchor[i].paStart, pSelAnchor[i].paEnd) > 0)
  55. {
  56. // free up all the anchors
  57. for (j=0; j<*pcFetched; j++)
  58. {
  59. SafeRelease(pSelAnchor[i].paStart);
  60. SafeRelease(pSelAnchor[i].paEnd);
  61. }
  62. hr = E_FAIL;
  63. goto Exit;
  64. }
  65. }
  66. // anchors -> ranges
  67. for (i=0; i<*pcFetched; i++)
  68. {
  69. range = new CRange;
  70. pSelection[i].range = (ITfRangeAnchor *)range;
  71. if (range == NULL ||
  72. !range->_InitWithDefaultGravity(this, OWN_ANCHORS, pSelAnchor[i].paStart, pSelAnchor[i].paEnd))
  73. {
  74. SafeRelease(range);
  75. SafeRelease(pSelAnchor[i].paStart);
  76. SafeRelease(pSelAnchor[i].paEnd);
  77. while (i>0) // need to free up all the ranges already allocated
  78. {
  79. pSelection[--i].range->Release();
  80. }
  81. hr = E_OUTOFMEMORY;
  82. goto Exit;
  83. }
  84. pSelection[i].style.ase = (TfActiveSelEnd)pSelAnchor[i].style.ase;
  85. pSelection[i].style.fInterimChar = pSelAnchor[i].style.fInterimChar;
  86. }
  87. Exit:
  88. if (hr != S_OK)
  89. {
  90. *pcFetched = 0;
  91. }
  92. if (pSelAnchor != &sel)
  93. {
  94. cicMemFree(pSelAnchor);
  95. }
  96. return hr;
  97. }
  98. //+---------------------------------------------------------------------------
  99. //
  100. // SetSelection
  101. //
  102. //----------------------------------------------------------------------------
  103. STDAPI CInputContext::SetSelection(TfEditCookie ec, ULONG ulCount, const TF_SELECTION *pSelection)
  104. {
  105. CRange *pRangeP;
  106. HRESULT hr;
  107. TS_SELECTION_ANCHOR sel;
  108. TS_SELECTION_ANCHOR *pSelAnchor;
  109. ULONG i;
  110. BOOL fPrevInterimChar;
  111. BOOL fEqual;
  112. IAnchor *paTest;
  113. LONG cchShift;
  114. if (ulCount == 0)
  115. return E_INVALIDARG;
  116. if (pSelection == NULL)
  117. return E_INVALIDARG;
  118. if (!_IsConnected())
  119. return TF_E_DISCONNECTED;
  120. if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
  121. {
  122. Assert(0);
  123. return TF_E_NOLOCK;
  124. }
  125. if (ulCount == 1)
  126. {
  127. pSelAnchor = &sel;
  128. }
  129. else if ((pSelAnchor = (TS_SELECTION_ANCHOR *)cicMemAlloc(ulCount*sizeof(TS_SELECTION_ANCHOR))) == NULL)
  130. return E_OUTOFMEMORY;
  131. // convert to TS_SELECTION_ANCHOR
  132. fPrevInterimChar = FALSE;
  133. for (i=0; i<ulCount; i++)
  134. {
  135. hr = E_INVALIDARG;
  136. if (pSelection[i].range == NULL)
  137. goto Exit;
  138. if ((pRangeP = GetCRange_NA(pSelection[i].range)) == NULL)
  139. goto Exit;
  140. pSelAnchor[i].paStart = pRangeP->_GetStart(); // no AddRef here
  141. pSelAnchor[i].paEnd = pRangeP->_GetEnd();
  142. if (pSelection[i].style.fInterimChar)
  143. {
  144. // verify this really has length 1, and there's only one in the array
  145. if (fPrevInterimChar)
  146. goto Exit;
  147. if (pSelection[i].style.ase != TS_AE_NONE)
  148. goto Exit;
  149. if (pSelAnchor[i].paStart->Clone(&paTest) == S_OK)
  150. {
  151. if (paTest->Shift(0, 1, &cchShift, NULL) != S_OK)
  152. goto EndTest;
  153. if (cchShift != 1)
  154. goto EndTest;
  155. if (paTest->IsEqual(pSelAnchor[i].paEnd, &fEqual) != S_OK || !fEqual)
  156. goto EndTest;
  157. hr = S_OK;
  158. EndTest:
  159. paTest->Release();
  160. if (hr != S_OK)
  161. goto Exit;
  162. }
  163. fPrevInterimChar = TRUE;
  164. }
  165. pSelAnchor[i].style.ase = (TsActiveSelEnd)pSelection[i].style.ase;
  166. pSelAnchor[i].style.fInterimChar = pSelection[i].style.fInterimChar;
  167. }
  168. hr = _ptsi->SetSelection(ulCount, pSelAnchor);
  169. if (hr != S_OK)
  170. goto Exit;
  171. // app won't notify us about sel changes we cause, so do that manually
  172. _OnSelectionChangeInternal(FALSE);
  173. Exit:
  174. if (pSelAnchor != &sel)
  175. {
  176. cicMemFree(pSelAnchor);
  177. }
  178. return hr;
  179. }
  180. //+---------------------------------------------------------------------------
  181. //
  182. // RequestEditSession
  183. //
  184. //----------------------------------------------------------------------------
  185. STDAPI CInputContext::RequestEditSession(TfClientId tid, ITfEditSession *pes, DWORD dwFlags, HRESULT *phrSession)
  186. {
  187. BOOL fForceAsync;
  188. TS_QUEUE_ITEM item;
  189. DWORD dwEditSessionFlagsOrg;
  190. if (phrSession == NULL)
  191. return E_INVALIDARG;
  192. *phrSession = E_FAIL;
  193. if (!_IsConnected())
  194. return TF_E_DISCONNECTED;
  195. if (pes == NULL ||
  196. (dwFlags & TF_ES_READ) == 0 ||
  197. (dwFlags & ~(TF_ES_SYNC | TF_ES_ASYNC | TF_ES_ALL_ACCESS_BITS)) != 0 ||
  198. ((dwFlags & TF_ES_SYNC) && (dwFlags & TF_ES_ASYNC)))
  199. {
  200. Assert(0);
  201. return E_INVALIDARG;
  202. }
  203. if (dwFlags & TF_ES_WRITE)
  204. {
  205. // TS_ES_WRITE implies TF_ES_PROPERTY_WRITE, so set it here to make life easier
  206. // for binary compat with cicero 1.0, we couldn't redefine TF_ES_READWRITE to include the third bit
  207. dwFlags |= TF_ES_PROPERTY_WRITE;
  208. }
  209. fForceAsync = (dwFlags & TF_ES_ASYNC);
  210. if ((dwFlags & (TF_ES_WRITE | TF_ES_PROPERTY_WRITE)) && (_dwEditSessionFlags & TF_ES_INNOTIFY))
  211. {
  212. // we're in _NotifyEndEdit or OnLayoutChange -- we only allow read locks here
  213. if (!(dwFlags & TF_ES_SYNC))
  214. {
  215. fForceAsync = TRUE;
  216. }
  217. else
  218. {
  219. Assert(0); // we can't do a synchronous write during a notification callback
  220. *phrSession = TF_E_SYNCHRONOUS;
  221. return S_OK;
  222. }
  223. }
  224. else if (!fForceAsync && (_dwEditSessionFlags & TF_ES_INEDITSESSION))
  225. {
  226. *phrSession = TF_E_LOCKED; // edit sessions are generally not re-entrant
  227. // no reentrancy if caller wants a write lock but current lock is read-only
  228. // nb: this explicitly disallows call stacks like: write-read-write, the
  229. // inner write would confuse the preceding reader, who doesn't expect changes
  230. if ((dwFlags & TF_ES_WRITE) && !(_dwEditSessionFlags & TF_ES_WRITE) ||
  231. (dwFlags & TF_ES_PROPERTY_WRITE) && !(_dwEditSessionFlags & TF_ES_PROPERTY_WRITE))
  232. {
  233. if (!(dwFlags & TF_ES_SYNC))
  234. {
  235. // request is TS_ES_ASYNCDONTCARE, so we'll make it async to recover
  236. fForceAsync = TRUE;
  237. goto QueueItem;
  238. }
  239. Assert(0);
  240. return TF_E_LOCKED;
  241. }
  242. // only allow reentrant write locks for the same tip
  243. if ((dwFlags & (TF_ES_WRITE | TF_ES_PROPERTY_WRITE)) && _tidInEditSession != tid)
  244. {
  245. Assert(0);
  246. return TF_E_LOCKED;
  247. }
  248. dwEditSessionFlagsOrg = _dwEditSessionFlags;
  249. // adjust read/write access for inner es
  250. _dwEditSessionFlags = (_dwEditSessionFlags & ~TF_ES_ALL_ACCESS_BITS) | (dwFlags & TF_ES_ALL_ACCESS_BITS);
  251. // ok, do it
  252. *phrSession = pes->DoEditSession(_ec);
  253. _dwEditSessionFlags = dwEditSessionFlagsOrg;
  254. return S_OK;
  255. }
  256. QueueItem:
  257. //
  258. // Don't queue the write lock item when the doc is read only.
  259. //
  260. if (dwFlags & TF_ES_WRITE)
  261. {
  262. TS_STATUS dcs;
  263. if (SUCCEEDED(GetStatus(&dcs)))
  264. {
  265. if (dcs.dwDynamicFlags & TF_SD_READONLY)
  266. {
  267. *phrSession = TS_E_READONLY;
  268. return S_OK;
  269. }
  270. }
  271. }
  272. item.pfnCallback = _EditSessionQiCallback;
  273. item.dwFlags = dwFlags;
  274. item.state.es.tid = tid;
  275. item.state.es.pes = pes;
  276. return _QueueItem(&item, fForceAsync, phrSession);
  277. }
  278. //+---------------------------------------------------------------------------
  279. //
  280. // _EditSessionQiCallback
  281. //
  282. //----------------------------------------------------------------------------
  283. /* static */
  284. HRESULT CInputContext::_EditSessionQiCallback(CInputContext *pic, TS_QUEUE_ITEM *pItem, QiCallbackCode qiCode)
  285. {
  286. HRESULT hr = S_OK;
  287. //
  288. // #489905
  289. //
  290. // we can not call sink anymore after DLL_PROCESS_DETACH.
  291. //
  292. if (DllShutdownInProgress())
  293. return hr;
  294. //
  295. // #507366
  296. //
  297. // Random AV happens with SPTIP's edit session.
  298. // #507366 might be fixed by #371798 (sptip). However it is nice to
  299. // have a pointer checking and protect the call by an exception handler.
  300. //
  301. if (!pItem->state.es.pes)
  302. return E_FAIL;
  303. switch (qiCode)
  304. {
  305. case QI_ADDREF:
  306. //
  307. // #507366
  308. //
  309. // Random AV happens with SPTIP's edit session.
  310. // #507366 might be fixed by #371798 (sptip). However it is nice to
  311. // have a pointer checking and protect the call by an exception
  312. // handler.
  313. //
  314. _try {
  315. pItem->state.es.pes->AddRef();
  316. }
  317. _except(1) {
  318. Assert(0);
  319. }
  320. break;
  321. case QI_DISPATCH:
  322. hr = pic->_DoEditSession(pItem->state.es.tid, pItem->state.es.pes, pItem->dwFlags);
  323. break;
  324. case QI_FREE:
  325. //
  326. // #507366
  327. //
  328. // Random AV happens with SPTIP's edit session.
  329. // #507366 might be fixed by #371798 (sptip). However it is nice to
  330. // have a pointer checking and protect the call by an exception
  331. // handler.
  332. //
  333. _try {
  334. pItem->state.es.pes->Release();
  335. }
  336. _except(1) {
  337. Assert(0);
  338. }
  339. break;
  340. default:
  341. Assert(0);
  342. }
  343. return hr;
  344. }
  345. //+---------------------------------------------------------------------------
  346. //
  347. // _PseudoSyncEditSessionQiCallback
  348. //
  349. //----------------------------------------------------------------------------
  350. /* static */
  351. HRESULT CInputContext::_PseudoSyncEditSessionQiCallback(CInputContext *_this, TS_QUEUE_ITEM *pItem, QiCallbackCode qiCode)
  352. {
  353. HRESULT hr;
  354. DWORD dwEditSessionFlags;
  355. TfClientId tidInEditSession;
  356. if (qiCode != QI_DISPATCH)
  357. return S_OK; // we can skip QI_ADDREF, QI_FREE since everything is synchronous/on the stack
  358. hr = S_OK;
  359. //
  360. // hook up fake ec here!
  361. //
  362. // NB: this code is very similar to _DoEditSession
  363. // make sure the logic stays consistent
  364. if (_this->_dwEditSessionFlags & TF_ES_INEDITSESSION)
  365. {
  366. Assert((TF_ES_WRITE & _this->_dwEditSessionFlags) ||
  367. !(TF_ES_WRITE & pItem->dwFlags));
  368. dwEditSessionFlags = _this->_dwEditSessionFlags;
  369. tidInEditSession = _this->_tidInEditSession;
  370. }
  371. else
  372. {
  373. dwEditSessionFlags = _this->_dwEditSessionFlags & ~TF_ES_ALL_ACCESS_BITS;
  374. tidInEditSession = TF_CLIENTID_NULL;
  375. }
  376. _this->_dwEditSessionFlags |= (TF_ES_INEDITSESSION | (pItem->dwFlags & TF_ES_ALL_ACCESS_BITS));
  377. _this->_tidInEditSession = g_gaSystem;
  378. //
  379. // dispatch
  380. //
  381. switch (pItem->state.pes.uCode)
  382. {
  383. case PSEUDO_ESCB_TERMCOMPOSITION:
  384. _this->_TerminateCompositionWithLock((ITfCompositionView *)pItem->state.pes.pvState, _this->_ec);
  385. break;
  386. case PSEUDO_ESCB_UPDATEKEYEVENTFILTER:
  387. _this->_UpdateKeyEventFilterCallback(_this->_ec);
  388. break;
  389. case PSEUDO_ESCB_GROWRANGE:
  390. GrowEmptyRangeByOneCallback(_this->_ec, (ITfRange *)pItem->state.pes.pvState);
  391. break;
  392. case PSEUDO_ESCB_BUILDOWNERRANGELIST:
  393. {
  394. BUILDOWNERRANGELISTQUEUEINFO *pbirl;
  395. pbirl = (BUILDOWNERRANGELISTQUEUEINFO *)(pItem->state.pes.pvState);
  396. pbirl->pFunc->BuildOwnerRangeListCallback(_this->_ec, _this, pbirl->pRange);
  397. }
  398. break;
  399. case PSEUDO_ESCB_SHIFTENDTORANGE:
  400. {
  401. SHIFTENDTORANGEQUEUEITEM *pqItemSER;
  402. pqItemSER = (SHIFTENDTORANGEQUEUEITEM*)(pItem->state.pes.pvState);
  403. pqItemSER->pRange->ShiftEndToRange(_this->_ec, pqItemSER->pRangeTo, pqItemSER->aPos);
  404. }
  405. break;
  406. case PSEUDO_ESCB_GETSELECTION:
  407. {
  408. GETSELECTIONQUEUEITEM *pqItemGS;
  409. pqItemGS = (GETSELECTIONQUEUEITEM *)(pItem->state.pes.pvState);
  410. GetSelectionSimple(_this->_ec, _this, pqItemGS->ppRange);
  411. break;
  412. }
  413. break;
  414. case PSEUDO_ESCB_SERIALIZE_ACP:
  415. {
  416. SERIALIZE_ACP_PARAMS *pParams = (SERIALIZE_ACP_PARAMS *)(pItem->state.pes.pvState);
  417. hr = pParams->pWrap->_Serialize(pParams->pProp, pParams->pRange, pParams->pHdr, pParams->pStream);
  418. break;
  419. }
  420. case PSEUDO_ESCB_SERIALIZE_ANCHOR:
  421. {
  422. SERIALIZE_ANCHOR_PARAMS *pParams = (SERIALIZE_ANCHOR_PARAMS *)(pItem->state.pes.pvState);
  423. hr = pParams->pProp->_Serialize(pParams->pRange, pParams->pHdr, pParams->pStream);
  424. break;
  425. }
  426. case PSEUDO_ESCB_UNSERIALIZE_ACP:
  427. {
  428. UNSERIALIZE_ACP_PARAMS *pParams = (UNSERIALIZE_ACP_PARAMS *)(pItem->state.pes.pvState);
  429. hr = pParams->pWrap->_Unserialize(pParams->pProp, pParams->pHdr, pParams->pStream, pParams->pLoaderACP);
  430. break;
  431. }
  432. case PSEUDO_ESCB_UNSERIALIZE_ANCHOR:
  433. {
  434. UNSERIALIZE_ANCHOR_PARAMS *pParams = (UNSERIALIZE_ANCHOR_PARAMS *)(pItem->state.pes.pvState);
  435. hr = pParams->pProp->_Unserialize(pParams->pHdr, pParams->pStream, pParams->pLoader);
  436. break;
  437. }
  438. case PSEUDO_ESCB_GETWHOLEDOCRANGE:
  439. {
  440. GETWHOLEDOCRANGE *pqItemGWDR;
  441. pqItemGWDR = (GETWHOLEDOCRANGE *)(pItem->state.pes.pvState);
  442. GetRangeForWholeDoc(_this->_ec, _this, pqItemGWDR->ppRange);
  443. }
  444. break;
  445. }
  446. //
  447. // notify/cleanup
  448. //
  449. if (pItem->dwFlags & (TF_ES_WRITE | TF_ES_PROPERTY_WRITE)) // don't bother if it was read-only
  450. {
  451. _this->_NotifyEndEdit();
  452. }
  453. if (tidInEditSession == TF_CLIENTID_NULL)
  454. _this->_IncEditCookie(); // next edit cookie value
  455. _this->_dwEditSessionFlags = dwEditSessionFlags;
  456. _this->_tidInEditSession = tidInEditSession;
  457. return hr;
  458. }
  459. //+---------------------------------------------------------------------------
  460. //
  461. // InWriteSession
  462. //
  463. //----------------------------------------------------------------------------
  464. STDAPI CInputContext::InWriteSession(TfClientId tid, BOOL *pfWriteSession)
  465. {
  466. if (pfWriteSession == NULL)
  467. return E_INVALIDARG;
  468. *pfWriteSession = (_dwEditSessionFlags & TF_ES_INEDITSESSION) &&
  469. (_tidInEditSession == tid) &&
  470. (_dwEditSessionFlags & (TF_ES_WRITE | TF_ES_PROPERTY_WRITE));
  471. Assert(!*pfWriteSession || tid != TF_CLIENTID_NULL); // should never return TRUE for TFCLIENTID_NULL
  472. // _tidInEditSession shouldn't be NULL if _dwEditSessionFlags & TF_ES_INEDITSESSION
  473. return S_OK;
  474. }
  475. //+---------------------------------------------------------------------------
  476. //
  477. // _DoEditSession
  478. //
  479. //----------------------------------------------------------------------------
  480. HRESULT CInputContext::_DoEditSession(TfClientId tid, ITfEditSession *pes, DWORD dwFlags)
  481. {
  482. HRESULT hr;
  483. // NB: this code is very similar to _PseudoSyncEditSessionQiCallback
  484. // make sure the logic stays consistent
  485. Assert(!(_dwEditSessionFlags & TF_ES_INEDITSESSION)); // shouldn't get this far
  486. Assert(_tidInEditSession == TF_CLIENTID_NULL || _tidInEditSession == g_gaApp); // there should never be another session in progress -- this is not a reentrant func
  487. _dwEditSessionFlags |= (TF_ES_INEDITSESSION | (dwFlags & TF_ES_ALL_ACCESS_BITS));
  488. _tidInEditSession = tid;
  489. //
  490. // #507366
  491. //
  492. // Random AV happens with SPTIP's edit session.
  493. // #507366 might be fixed by #371798 (sptip). However it is nice to
  494. // have a pointer checking and protect the call by an exception
  495. // handler.
  496. //
  497. _try {
  498. hr = pes->DoEditSession(_ec);
  499. }
  500. _except(1) {
  501. hr = E_FAIL;
  502. }
  503. // app won't notify us about our own lock release, so do it manually
  504. if (dwFlags & (TF_ES_WRITE | TF_ES_PROPERTY_WRITE)) // don't bother if it was read-only
  505. {
  506. _NotifyEndEdit();
  507. }
  508. _IncEditCookie(); // next edit cookie value
  509. _dwEditSessionFlags &= ~(TF_ES_INEDITSESSION | TF_ES_ALL_ACCESS_BITS);
  510. _tidInEditSession = TF_CLIENTID_NULL;
  511. return hr;
  512. }
  513. //+---------------------------------------------------------------------------
  514. //
  515. // _NotifyEndEdit
  516. //
  517. // Returns TRUE iff there were changes.
  518. //----------------------------------------------------------------------------
  519. BOOL CInputContext::_NotifyEndEdit(void)
  520. {
  521. CRange *pRange;
  522. CProperty *prop;
  523. int i;
  524. int cTextSpans;
  525. SPAN *pSpan;
  526. CSpanSet *pssText;
  527. CSpanSet *pssProperty;
  528. DWORD dwOld;
  529. CStructArray<GENERICSINK> *prgSinks;
  530. BOOL fChanges = FALSE;
  531. if (!_IsConnected())
  532. return FALSE; // we've been disconnected, nothing to notify
  533. if (!EnsureEditRecord())
  534. return FALSE; // low mem.
  535. if (_pEditRecord->_GetSelectionStatus())
  536. {
  537. // we let keystroke manager to update _gaKeyEventFilterTIP
  538. // since selection was changed.
  539. _fInvalidKeyEventFilterTIP = TRUE;
  540. }
  541. // only allow read locks during the notification
  542. // if we're in an edit session, keep using the same lock
  543. _dwEditSessionFlags |= TF_ES_INNOTIFY;
  544. pssText = _pEditRecord->_GetTextSpanSet();
  545. cTextSpans = pssText->GetCount();
  546. // if we are not in the edit session, we need to make
  547. // _PropertyTextUpdate. The app has clobbered some text.
  548. if (!(_dwEditSessionFlags & TF_ES_INEDITSESSION))
  549. {
  550. pSpan = pssText->GetSpans();
  551. for (i = 0; i < cTextSpans; i++)
  552. {
  553. _PropertyTextUpdate(pSpan->dwFlags, pSpan->paStart, pSpan->paEnd);
  554. pSpan++;
  555. }
  556. }
  557. // do the ITfRangeChangeSink::OnChange notifications
  558. if (cTextSpans > 0)
  559. {
  560. fChanges = TRUE;
  561. for (pRange = _pOnChangeRanges; pRange != NULL; pRange = pRange->_GetNextOnChangeRangeInIcsub())
  562. {
  563. if (!pRange->_IsDirty())
  564. continue;
  565. pRange->_ClearDirty();
  566. prgSinks = pRange->_GetChangeSinks();
  567. Assert(prgSinks); // shouldn't be on the list if this is NULL
  568. Assert(prgSinks->Count() > 0); // shouldn't be on the list if this is 0
  569. for (i=0; i<prgSinks->Count(); i++)
  570. {
  571. ((ITfRangeChangeSink *)prgSinks->GetPtr(i)->pSink)->OnChange((ITfRangeAnchor *)pRange);
  572. }
  573. }
  574. }
  575. // accumulate the property span sets into _pEditRecord
  576. for (prop = _pPropList; prop != NULL; prop = prop->_pNext)
  577. {
  578. if ((pssProperty = prop->_GetSpanSet()) == NULL ||
  579. pssProperty->GetCount() == 0)
  580. {
  581. continue; // no delta
  582. }
  583. fChanges = TRUE;
  584. _pEditRecord->_AddProperty(prop->GetPropGuidAtom(), pssProperty);
  585. }
  586. if (!_pEditRecord->_IsEmpty()) // just a perf thing
  587. {
  588. // do the OnEndEdit notifications
  589. prgSinks = _GetTextEditSinks();
  590. dwOld = _dwEditSessionFlags;
  591. _dwEditSessionFlags = (TF_ES_READWRITE | TF_ES_PROPERTY_WRITE | TF_ES_INNOTIFY | TF_ES_INEDITSESSION);
  592. for (i=0; i<prgSinks->Count(); i++)
  593. {
  594. ((ITfTextEditSink *)prgSinks->GetPtr(i)->pSink)->OnEndEdit(this, _ec, _pEditRecord);
  595. }
  596. _dwEditSessionFlags = dwOld;
  597. // properties need to either stop referencing their span sets, or reset them
  598. for (prop = _pPropList; prop != NULL; prop = prop->_pNext)
  599. {
  600. prop->_Dbg_AssertNoChangeHistory();
  601. if ((pssProperty = prop->_GetSpanSet()) == NULL ||
  602. pssProperty->GetCount() == 0)
  603. {
  604. continue; // no delta
  605. }
  606. if (_pEditRecord->_SecondRef())
  607. {
  608. prop->_ClearSpanSet();
  609. }
  610. else
  611. {
  612. prop->_ResetSpanSet();
  613. }
  614. }
  615. if (!_pEditRecord->_SecondRef())
  616. {
  617. _pEditRecord->_Reset();
  618. }
  619. else
  620. {
  621. // someone still holds a ref, so need a new edit record
  622. _pEditRecord->Release();
  623. _pEditRecord = new CEditRecord(this); // Issue: delay load! Issue: handle out of mem
  624. }
  625. }
  626. // status change sinks
  627. if (_fStatusChanged)
  628. {
  629. _fStatusChanged = FALSE;
  630. _OnStatusChangeInternal();
  631. }
  632. // layout change sinks
  633. if (_fLayoutChanged)
  634. {
  635. _fLayoutChanged = FALSE;
  636. // for cicero 1, we only support one view
  637. // eventually we'll need a list of all affected views...not just the default view..and also create/destroy
  638. TsViewCookie vcActiveView;
  639. if (_ptsi->GetActiveView(&vcActiveView) == S_OK)
  640. {
  641. _OnLayoutChangeInternal(TS_LC_CHANGE, vcActiveView);
  642. }
  643. else
  644. {
  645. Assert(0); // how did GetActiveView fail?
  646. }
  647. }
  648. // clear the read-only block
  649. _dwEditSessionFlags &= ~TF_ES_INNOTIFY;
  650. return fChanges;
  651. }
  652. //+---------------------------------------------------------------------------
  653. //
  654. // OnTextChange
  655. //
  656. // We only get here from ITextStoreAnchorSink. We don't have a lock!
  657. //----------------------------------------------------------------------------
  658. STDAPI CInputContext::OnTextChange(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd)
  659. {
  660. HRESULT hr;
  661. SPAN *span;
  662. Assert((dwFlags & ~TS_TC_CORRECTION) == 0);
  663. if (_IsInEditSession())
  664. {
  665. Assert(0); // someone other than cicero is editing the doc while cicero holds a lock
  666. return TS_E_NOLOCK;
  667. }
  668. // record this change
  669. if ((span = _rgAppTextChanges.Append(1)) == NULL)
  670. return E_OUTOFMEMORY;
  671. if (paStart->Clone(&span->paStart) != S_OK || span->paStart == NULL)
  672. goto ExitError;
  673. if (paEnd->Clone(&span->paEnd) != S_OK || span->paEnd == NULL)
  674. goto ExitError;
  675. span->dwFlags = dwFlags;
  676. // get a lock eventually so we can deal with the changes
  677. SafeRequestLock(_ptsi, TS_LF_READ, &hr);
  678. return S_OK;
  679. ExitError:
  680. SafeRelease(span->paStart);
  681. SafeRelease(span->paEnd);
  682. Assert(_rgAppTextChanges.Count() > 0);
  683. _rgAppTextChanges.Remove(_rgAppTextChanges.Count()-1, 1);
  684. return E_FAIL;
  685. }
  686. //+---------------------------------------------------------------------------
  687. //
  688. // _OnTextChangeInternal
  689. //
  690. // Unlike OnTextChange, here we know it's safe to call IAnchor::Compare.
  691. // We either got here from an ITfRange method, or from a wrapped ITextStoreACP.
  692. //----------------------------------------------------------------------------
  693. HRESULT CInputContext::_OnTextChangeInternal(DWORD dwFlags, IAnchor *paStart, IAnchor *paEnd, AnchorOwnership ao)
  694. {
  695. Assert((dwFlags & ~TS_TC_CORRECTION) == 0);
  696. if (!EnsureEditRecord())
  697. return E_OUTOFMEMORY;
  698. // track the delta
  699. _pEditRecord->_GetTextSpanSet()->Add(dwFlags, paStart, paEnd, ao);
  700. // mark any appropriate ranges dirty
  701. // perf: do this after the edit session ends! fewer calls that way...
  702. _MarkDirtyRanges(paStart, paEnd);
  703. return S_OK;
  704. }
  705. //+---------------------------------------------------------------------------
  706. //
  707. // OnSelectionChange
  708. //
  709. //----------------------------------------------------------------------------
  710. STDAPI CInputContext::OnSelectionChange()
  711. {
  712. if (_IsInEditSession())
  713. {
  714. Assert(0); // someone other than cicero is editing the doc while cicero holds a lock
  715. return TS_E_NOLOCK;
  716. }
  717. return _OnSelectionChangeInternal(TRUE);
  718. }
  719. //+---------------------------------------------------------------------------
  720. //
  721. // _OnSelectionChangeInternal
  722. //
  723. //----------------------------------------------------------------------------
  724. HRESULT CInputContext::_OnSelectionChangeInternal(BOOL fAppChange)
  725. {
  726. HRESULT hr;
  727. if (!EnsureEditRecord())
  728. return E_OUTOFMEMORY;
  729. _pEditRecord->_SetSelectionStatus();
  730. if (fAppChange) // perf: could we use _fLockHeld and do away with the fAppChange param?
  731. {
  732. // get a lock eventually so we can deal with the changes
  733. SafeRequestLock(_ptsi, TS_LF_READ, &hr);
  734. }
  735. return S_OK;
  736. }
  737. //+---------------------------------------------------------------------------
  738. //
  739. // OnLockGranted
  740. //
  741. //----------------------------------------------------------------------------
  742. STDAPI CInputContext::OnLockGranted(DWORD dwLockFlags)
  743. {
  744. BOOL fAppChangesSent;
  745. BOOL fAppCall;
  746. HRESULT hr;
  747. if ((dwLockFlags & ~(TS_LF_SYNC | TS_LF_READWRITE)) != 0)
  748. {
  749. Assert(0); // bogus dwLockFlags param
  750. return E_INVALIDARG;
  751. }
  752. if ((dwLockFlags & TS_LF_READWRITE) == 0)
  753. {
  754. Assert(0); // bogus dwLockFlags param
  755. return E_INVALIDARG;
  756. }
  757. #ifdef DEBUG
  758. // we don't really need to check for reentrancy since
  759. // the app is not supposed to call back into us, but
  760. // why not be paranoid?
  761. // Issue: for robustness, do something in retail
  762. Assert(!_dbg_fInOnLockGranted) // no reentrancy
  763. _dbg_fInOnLockGranted = TRUE;
  764. #endif // DEBUG
  765. fAppChangesSent = FALSE;
  766. fAppCall = FALSE;
  767. if (_fLockHeld == FALSE)
  768. {
  769. fAppCall = TRUE;
  770. _fLockHeld = TRUE;
  771. _dwlt = dwLockFlags;
  772. fAppChangesSent = _SynchAppChanges(dwLockFlags);
  773. }
  774. // hr will hold result of any synch queue item, need to return this!
  775. hr = _EmptyLockQueue(dwLockFlags, fAppChangesSent);
  776. if (fAppCall)
  777. {
  778. _fLockHeld = FALSE;
  779. }
  780. #ifdef DEBUG
  781. _dbg_fInOnLockGranted = FALSE;
  782. #endif // DEBUG
  783. return hr;
  784. }
  785. //+---------------------------------------------------------------------------
  786. //
  787. // _SynchAppChanges
  788. //
  789. //----------------------------------------------------------------------------
  790. BOOL CInputContext::_SynchAppChanges(DWORD dwLockFlags)
  791. {
  792. TfClientId tidInEditSessionOrg;
  793. int i;
  794. SPAN *span;
  795. BOOL fAppChangesSent;
  796. if (!EnsureEditRecord())
  797. return FALSE;
  798. // check for cached app text changes
  799. for (i=0; i<_rgAppTextChanges.Count(); i++)
  800. {
  801. span = _rgAppTextChanges.GetPtr(i);
  802. // track the delta
  803. // NB: Add takes ownership of anchors here! So we don't release them...
  804. _pEditRecord->_GetTextSpanSet()->Add(span->dwFlags, span->paStart, span->paEnd, OWN_ANCHORS);
  805. // mark any appropriate ranges dirty
  806. _MarkDirtyRanges(span->paStart, span->paEnd);
  807. }
  808. // all done with the app changes!
  809. _rgAppTextChanges.Clear();
  810. // at this point ranges with TF_GRAVITY_FORWARD, TF_GRAVITY_BACKWARD could
  811. // have crossed anchors (this can only happen in response to app changes,
  812. // so we check here instead of in _NotifyEndEdit, which can be called after
  813. // a SetText, etc.). We track this with a lazy test in the range obj
  814. // based on an id.
  815. if (++_dwLastLockReleaseID == 0xffffffff)
  816. {
  817. Assert(0); // Issue: need code here to handle wrap-around, prob. need to notify all range objects
  818. }
  819. // deal with any app changes, need to send notifications
  820. // theoretically, we only need to make this call when _tidInEditSession == TF_CLIENTID_NULL
  821. // (not inside _DoEditSession, a call from the app) but we'll make it anyways to deal with app bugs
  822. // App bug: if the app has pending changes but grants a synchronous lock, we'll announce the changes
  823. // here even though we're in an edit session, then return error below...
  824. tidInEditSessionOrg = _tidInEditSession;
  825. _tidInEditSession = g_gaApp;
  826. fAppChangesSent = _NotifyEndEdit();
  827. _tidInEditSession = tidInEditSessionOrg;
  828. return fAppChangesSent;
  829. }
  830. //+---------------------------------------------------------------------------
  831. //
  832. // ITfContextOwnerServices::OnLayoutChange
  833. //
  834. //----------------------------------------------------------------------------
  835. STDAPI CInputContext::OnLayoutChange()
  836. {
  837. // the default impl always has just one view,
  838. // so specify it directly
  839. return OnLayoutChange(TS_LC_CHANGE, TSI_ACTIVE_VIEW_COOKIE);
  840. }
  841. //+---------------------------------------------------------------------------
  842. //
  843. // IDocCommonSinkAnchor::OnLayoutChange
  844. //
  845. //----------------------------------------------------------------------------
  846. STDAPI CInputContext::OnLayoutChange(TsLayoutCode lcode, TsViewCookie vcView)
  847. {
  848. HRESULT hr;
  849. _fLayoutChanged = TRUE;
  850. // for now (cicero 1), ignoring views other than the default!
  851. // todo: need to keep a list of all affected views
  852. if (!_fLockHeld) // might hold lock if ic owner is making modifications
  853. {
  854. // get a lock eventually so we can deal with the changes
  855. SafeRequestLock(_ptsi, TS_LF_READ, &hr);
  856. }
  857. return S_OK;
  858. }
  859. //+---------------------------------------------------------------------------
  860. //
  861. // OnStatusChange
  862. //
  863. //----------------------------------------------------------------------------
  864. STDAPI CInputContext::OnStatusChange(DWORD dwFlags)
  865. {
  866. HRESULT hr;
  867. _fStatusChanged = TRUE;
  868. _dwStatusChangedFlags |= dwFlags;
  869. if (!_fLockHeld) // might hold lock if ic owner is making modifications
  870. {
  871. // get a lock eventually so we can deal with the changes
  872. SafeRequestLock(_ptsi, TS_LF_READ, &hr);
  873. }
  874. return S_OK;
  875. }
  876. //+---------------------------------------------------------------------------
  877. //
  878. // OnAttrsChange
  879. //
  880. //----------------------------------------------------------------------------
  881. STDAPI CInputContext::OnAttrsChange(IAnchor *paStart, IAnchor *paEnd, ULONG cAttrs, const TS_ATTRID *paAttrs)
  882. {
  883. CSpanSet *pss;
  884. ULONG i;
  885. TfGuidAtom gaType;
  886. HRESULT hr;
  887. //
  888. // Issue: need to delay any work until we have a lock!, just like text deltas
  889. //
  890. // paStart, paEnd can be NULL if both are NULL -> whole doc
  891. if ((paStart == NULL && paEnd != NULL) ||
  892. (paStart != NULL && paEnd == NULL))
  893. {
  894. return E_INVALIDARG;
  895. }
  896. if (cAttrs == 0)
  897. return S_OK;
  898. if (paAttrs == NULL)
  899. return E_INVALIDARG;
  900. if (!EnsureEditRecord())
  901. return E_OUTOFMEMORY;
  902. // record the change
  903. for (i=0; i<cAttrs; i++)
  904. {
  905. if (MyRegisterGUID(paAttrs[i], &gaType) != S_OK)
  906. continue;
  907. if (pss = _pEditRecord->_FindCreateAppAttr(gaType))
  908. {
  909. pss->Add(0, paStart, paEnd, COPY_ANCHORS);
  910. }
  911. }
  912. if (!_fLockHeld) // might hold lock if ic owner is making modifications
  913. {
  914. // get a lock eventually so we can deal with the changes
  915. SafeRequestLock(_ptsi, TS_LF_READ, &hr);
  916. }
  917. return S_OK;
  918. }
  919. //+---------------------------------------------------------------------------
  920. //
  921. // OnAttributeChange
  922. //
  923. // Called when sys attr changes for cicero default tsi.
  924. //----------------------------------------------------------------------------
  925. HRESULT CInputContext::OnAttributeChange(REFGUID rguidAttr)
  926. {
  927. return OnAttrsChange(NULL, NULL, 1, &rguidAttr);
  928. }
  929. //+---------------------------------------------------------------------------
  930. //
  931. // OnStartEditTransaction
  932. //
  933. //----------------------------------------------------------------------------
  934. STDAPI CInputContext::OnStartEditTransaction()
  935. {
  936. int i;
  937. CStructArray<GENERICSINK> *prgSinks;
  938. if (_cRefEditTransaction++ > 0)
  939. return S_OK;
  940. prgSinks = _GetEditTransactionSink();
  941. for (i=0; i<prgSinks->Count(); i++)
  942. {
  943. ((ITfEditTransactionSink *)prgSinks->GetPtr(i)->pSink)->OnStartEditTransaction(this);
  944. }
  945. return S_OK;
  946. }
  947. //+---------------------------------------------------------------------------
  948. //
  949. // OnEndEditTransaction
  950. //
  951. //----------------------------------------------------------------------------
  952. STDAPI CInputContext::OnEndEditTransaction()
  953. {
  954. int i;
  955. CStructArray<GENERICSINK> *prgSinks;
  956. if (_cRefEditTransaction <= 0)
  957. {
  958. Assert(0); // bogus ref count
  959. return E_UNEXPECTED;
  960. }
  961. if (_cRefEditTransaction > 1)
  962. goto Exit;
  963. prgSinks = _GetEditTransactionSink();
  964. for (i=0; i<prgSinks->Count(); i++)
  965. {
  966. ((ITfEditTransactionSink *)prgSinks->GetPtr(i)->pSink)->OnEndEditTransaction(this);
  967. }
  968. Exit:
  969. // dec the ref to 0 last, to prevent reentrancy
  970. _cRefEditTransaction--;
  971. return S_OK;
  972. }
  973. //+---------------------------------------------------------------------------
  974. //
  975. // _OnLayoutChangeInternal
  976. //
  977. //----------------------------------------------------------------------------
  978. HRESULT CInputContext::_OnLayoutChangeInternal(TsLayoutCode lcode, TsViewCookie vcView)
  979. {
  980. DWORD dwOld;
  981. CStructArray<GENERICSINK> *prgSinks;
  982. int i;
  983. ITfContextView *pView = NULL; // compiler "uninitialized var" warning
  984. // xlate the view
  985. GetActiveView(&pView); // when we support multiple views, need to actually use vcView
  986. if (pView == NULL)
  987. return E_OUTOFMEMORY;
  988. // only allow read locks during the notification
  989. // we might have the read-only bit set already, so save the
  990. // old value
  991. dwOld = _dwEditSessionFlags;
  992. _dwEditSessionFlags |= TF_ES_INNOTIFY;
  993. prgSinks = _GetTextLayoutSinks();
  994. for (i=0; i<prgSinks->Count(); i++)
  995. {
  996. ((ITfTextLayoutSink *)prgSinks->GetPtr(i)->pSink)->OnLayoutChange(this, (TfLayoutCode)lcode, pView);
  997. }
  998. pView->Release();
  999. // clear the read-only block
  1000. _dwEditSessionFlags = dwOld;
  1001. return S_OK;
  1002. }
  1003. //+---------------------------------------------------------------------------
  1004. //
  1005. // _OnStatusChangeInternal
  1006. //
  1007. //----------------------------------------------------------------------------
  1008. HRESULT CInputContext::_OnStatusChangeInternal()
  1009. {
  1010. DWORD dwOld;
  1011. CStructArray<GENERICSINK> *prgSinks;
  1012. int i;
  1013. Assert((_dwEditSessionFlags & TF_ES_INEDITSESSION) == 0); // we must never hold a lock when we do the callbacks
  1014. // only allow read locks during the notification
  1015. // we might have the read-only bit set already, so save the
  1016. // old value
  1017. dwOld = _dwEditSessionFlags;
  1018. _dwEditSessionFlags |= TF_ES_INNOTIFY;
  1019. prgSinks = _GetStatusSinks();
  1020. for (i=0; i<prgSinks->Count(); i++)
  1021. {
  1022. ((ITfStatusSink *)prgSinks->GetPtr(i)->pSink)->OnStatusChange(this, _dwStatusChangedFlags);
  1023. }
  1024. _dwStatusChangedFlags = 0;
  1025. // clear the read-only block
  1026. _dwEditSessionFlags = dwOld;
  1027. return S_OK;
  1028. }
  1029. //+---------------------------------------------------------------------------
  1030. //
  1031. // Serialize
  1032. //
  1033. //----------------------------------------------------------------------------
  1034. STDAPI CInputContext::Serialize(ITfProperty *pProp, ITfRange *pRange, TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *pHdr, IStream *pStream)
  1035. {
  1036. SERIALIZE_ANCHOR_PARAMS params;
  1037. HRESULT hr;
  1038. CProperty *pPropP;
  1039. CRange *pCRange;
  1040. if ((pCRange = GetCRange_NA(pRange)) == NULL)
  1041. return E_INVALIDARG;
  1042. if (!VerifySameContext(this, pCRange))
  1043. return E_INVALIDARG;
  1044. if ((pPropP = GetCProperty(pProp)) == NULL)
  1045. return E_INVALIDARG;
  1046. params.pProp = pPropP;
  1047. params.pRange = pCRange;
  1048. params.pHdr = pHdr;
  1049. params.pStream = pStream;
  1050. hr = S_OK;
  1051. // need a sync read lock to do our work
  1052. if (_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_SERIALIZE_ANCHOR, &params, &hr) != S_OK)
  1053. {
  1054. Assert(0); // app won't give us a sync read lock
  1055. hr = E_FAIL;
  1056. }
  1057. SafeRelease(pPropP);
  1058. return hr;
  1059. }
  1060. //+---------------------------------------------------------------------------
  1061. //
  1062. // Unserialize
  1063. //
  1064. //----------------------------------------------------------------------------
  1065. STDAPI CInputContext::Unserialize(ITfProperty *pProp, const TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *pHdr, IStream *pStream, ITfPersistentPropertyLoaderAnchor *pLoader)
  1066. {
  1067. CProperty *pPropP;
  1068. UNSERIALIZE_ANCHOR_PARAMS params;
  1069. HRESULT hr;
  1070. if ((pPropP = GetCProperty(pProp)) == NULL)
  1071. return E_INVALIDARG;
  1072. params.pProp = pPropP;
  1073. params.pHdr = pHdr;
  1074. params.pStream = pStream;
  1075. params.pLoader = pLoader;
  1076. // need a sync read lock to do our work
  1077. if (_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_UNSERIALIZE_ANCHOR, &params, &hr) != S_OK)
  1078. {
  1079. Assert(0); // app won't give us a sync read lock
  1080. return E_FAIL;
  1081. }
  1082. return hr;
  1083. }
  1084. //+---------------------------------------------------------------------------
  1085. //
  1086. // ForceLoadProperty
  1087. //
  1088. //----------------------------------------------------------------------------
  1089. STDAPI CInputContext::ForceLoadProperty(ITfProperty *pProp)
  1090. {
  1091. CProperty *pPropP;
  1092. HRESULT hr;
  1093. if ((pPropP = GetCProperty(pProp)) == NULL)
  1094. return E_INVALIDARG;
  1095. hr = pPropP->ForceLoad();
  1096. pPropP->Release();
  1097. return hr;
  1098. }
  1099. //+---------------------------------------------------------------------------
  1100. //
  1101. // _MarkDirtyRanges
  1102. //
  1103. //----------------------------------------------------------------------------
  1104. void CInputContext::_MarkDirtyRanges(IAnchor *paStart, IAnchor *paEnd)
  1105. {
  1106. CRange *range;
  1107. IAnchor *paRangeStart;
  1108. IAnchor *paRangeEnd;
  1109. DWORD dwHistory;
  1110. BOOL fDirty;
  1111. // we're only interested in ranges that have notification sinks
  1112. // perf: it would be cool avoid checking ranges based on some ordering scheme....
  1113. for (range = _pOnChangeRanges; range != NULL; range = range->_GetNextOnChangeRangeInIcsub())
  1114. {
  1115. if (range->_IsDirty())
  1116. continue;
  1117. fDirty = FALSE;
  1118. paRangeStart = range->_GetStart();
  1119. paRangeEnd = range->_GetEnd();
  1120. // check BOTH anchors for deletions -- need to clear both
  1121. // no matter what
  1122. if (paRangeStart->GetChangeHistory(&dwHistory) == S_OK &&
  1123. (dwHistory & TS_CH_FOLLOWING_DEL))
  1124. {
  1125. paRangeStart->ClearChangeHistory();
  1126. fDirty = TRUE;
  1127. }
  1128. if (paRangeEnd->GetChangeHistory(&dwHistory) == S_OK &&
  1129. (dwHistory & TS_CH_PRECEDING_DEL))
  1130. {
  1131. paRangeEnd->ClearChangeHistory();
  1132. fDirty = TRUE;
  1133. }
  1134. // even if no anchors collapsed, the range may overlap a delta
  1135. if (!fDirty)
  1136. {
  1137. if (CompareAnchors(paRangeEnd, paStart) > 0 &&
  1138. CompareAnchors(paRangeStart, paEnd) < 0)
  1139. {
  1140. fDirty = TRUE;
  1141. }
  1142. }
  1143. if (fDirty)
  1144. {
  1145. range->_SetDirty();
  1146. }
  1147. }
  1148. }
  1149. //+---------------------------------------------------------------------------
  1150. //
  1151. // UpdateKeyEventFilter
  1152. //
  1153. //----------------------------------------------------------------------------
  1154. void CInputContext::_UpdateKeyEventFilter()
  1155. {
  1156. HRESULT hr;
  1157. // Our cache _gaKeyEventFilterTTIP is valid so just return TRUE.
  1158. if (!_fInvalidKeyEventFilterTIP)
  1159. return;
  1160. _gaKeyEventFilterTIP[0] = TF_INVALID_GUIDATOM;
  1161. _gaKeyEventFilterTIP[1] = TF_INVALID_GUIDATOM;
  1162. if (_DoPseudoSyncEditSession(TF_ES_READ,
  1163. PSEUDO_ESCB_UPDATEKEYEVENTFILTER,
  1164. NULL,
  1165. &hr) != S_OK || hr != S_OK)
  1166. {
  1167. //
  1168. // Isn't application ready to give lock?
  1169. //
  1170. Assert(0);
  1171. }
  1172. }
  1173. //+---------------------------------------------------------------------------
  1174. //
  1175. // _UpdateKeyEventFilterCallback
  1176. //
  1177. //----------------------------------------------------------------------------
  1178. HRESULT CInputContext::_UpdateKeyEventFilterCallback(TfEditCookie ec)
  1179. {
  1180. TF_SELECTION sel;
  1181. ULONG cFetched;
  1182. BOOL fEmpty;
  1183. // perf: we don't really need to create a range here, we just want the anchors
  1184. if (GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &cFetched) == S_OK && cFetched == 1)
  1185. {
  1186. HRESULT hr;
  1187. BOOL bRightSide= TRUE;
  1188. BOOL bLeftSide= TRUE;
  1189. //
  1190. // If the current selection is not empty, we just interested in
  1191. // the caret position.
  1192. //
  1193. hr = sel.range->IsEmpty(ec, &fEmpty);
  1194. if ((hr == S_OK) && !fEmpty)
  1195. {
  1196. if (sel.style.ase == TF_AE_START)
  1197. {
  1198. hr = sel.range->ShiftEndToRange(ec,
  1199. sel.range,
  1200. TF_ANCHOR_START);
  1201. bRightSide = FALSE;
  1202. }
  1203. else if (sel.style.ase == TF_AE_END)
  1204. {
  1205. hr = sel.range->ShiftStartToRange(ec,
  1206. sel.range,
  1207. TF_ANCHOR_END);
  1208. bLeftSide = FALSE;
  1209. }
  1210. }
  1211. if (SUCCEEDED(hr))
  1212. {
  1213. if (_pPropTextOwner)
  1214. {
  1215. CRange *pPropRange;
  1216. CRange *pSelRange = GetCRange_NA(sel.range);
  1217. Assert(pSelRange != NULL); // we just created this guy
  1218. if (bRightSide)
  1219. {
  1220. //
  1221. // Find the right side owner of sel.
  1222. // try the start edge of the property so fEnd is FALSE.
  1223. //
  1224. if (_pPropTextOwner->_InternalFindRange(pSelRange,
  1225. &pPropRange,
  1226. TF_ANCHOR_END,
  1227. FALSE) == S_OK)
  1228. {
  1229. VARIANT var;
  1230. if (_pPropTextOwner->GetValue(ec, (ITfRangeAnchor *)pPropRange, &var) == S_OK)
  1231. {
  1232. IAnchor *paEnd;
  1233. CRange *pCRangeSel;
  1234. Assert(var.vt == VT_I4);
  1235. _gaKeyEventFilterTIP[LEFT_FILTERTIP] = (TfGuidAtom)var.lVal;
  1236. // don't need to VariantClear because it's VT_I4
  1237. //
  1238. // If the end of this proprange is left side of
  1239. // the caret, the left side owner will be same.
  1240. // so we don't have to find left proprange then.
  1241. //
  1242. paEnd = pPropRange->_GetEnd();
  1243. if (paEnd && (pCRangeSel = GetCRange_NA(sel.range)))
  1244. {
  1245. if (CompareAnchors(paEnd, pCRangeSel->_GetStart()) > 0)
  1246. bLeftSide = FALSE;
  1247. }
  1248. }
  1249. pPropRange->Release();
  1250. }
  1251. }
  1252. if (bLeftSide)
  1253. {
  1254. //
  1255. // Find the left side owner of sel.
  1256. // try the end edge of the property so fEnd is TRUE.
  1257. //
  1258. if (_pPropTextOwner->_InternalFindRange(pSelRange,
  1259. &pPropRange,
  1260. TF_ANCHOR_START,
  1261. TRUE) == S_OK)
  1262. {
  1263. VARIANT var;
  1264. if (_pPropTextOwner->GetValue(ec, (ITfRangeAnchor *)pPropRange, &var) == S_OK)
  1265. {
  1266. Assert(var.vt == VT_I4);
  1267. if (_gaKeyEventFilterTIP[LEFT_FILTERTIP] != (TfGuidAtom)var.lVal)
  1268. {
  1269. _gaKeyEventFilterTIP[RIGHT_FILTERTIP] = (TfGuidAtom)var.lVal;
  1270. }
  1271. // don't need to VariantClear because it's VT_I4
  1272. }
  1273. pPropRange->Release();
  1274. }
  1275. }
  1276. }
  1277. }
  1278. sel.range->Release();
  1279. }
  1280. _fInvalidKeyEventFilterTIP = FALSE;
  1281. return S_OK;
  1282. }