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.

1602 lines
40 KiB

  1. //
  2. // range.cpp
  3. //
  4. #include "private.h"
  5. #include "range.h"
  6. #include "ic.h"
  7. #include "immxutil.h"
  8. #include "rprop.h"
  9. #include "tim.h"
  10. #include "anchoref.h"
  11. #include "compose.h"
  12. /* b68832f0-34b9-11d3-a745-0050040ab407 */
  13. const IID IID_PRIV_CRANGE = { 0xb68832f0,0x34b9, 0x11d3, {0xa7, 0x45, 0x00, 0x50, 0x04, 0x0a, 0xb4, 0x07} };
  14. DBG_ID_INSTANCE(CRange);
  15. MEMCACHE *CRange::_s_pMemCache = NULL;
  16. //+---------------------------------------------------------------------------
  17. //
  18. // _InitClass
  19. //
  20. //----------------------------------------------------------------------------
  21. /* static */
  22. void CRange::_InitClass()
  23. {
  24. _s_pMemCache = MemCache_New(32);
  25. }
  26. //+---------------------------------------------------------------------------
  27. //
  28. // _UninitClass
  29. //
  30. //----------------------------------------------------------------------------
  31. /* static */
  32. void CRange::_UninitClass()
  33. {
  34. if (_s_pMemCache == NULL)
  35. return;
  36. MemCache_Delete(_s_pMemCache);
  37. _s_pMemCache = NULL;
  38. }
  39. //+---------------------------------------------------------------------------
  40. //
  41. // _Init
  42. //
  43. // NB: If fSetDefaultGravity == TRUE, make certain paStart <= paEnd, or you
  44. // will break something!
  45. //----------------------------------------------------------------------------
  46. BOOL CRange::_Init(CInputContext *pic, AnchorOwnership ao, IAnchor *paStart, IAnchor *paEnd, RInit rinit)
  47. {
  48. TsGravity gStart;
  49. TsGravity gEnd;
  50. // can't check the anchors because we may be cloned from a range with crossed anchors
  51. // can't do anything about crossed anchors until we know we have a doc lock
  52. //Assert(CompareAnchors(paStart, paEnd) <= 0);
  53. Assert(_paStart == NULL);
  54. Assert(_paEnd == NULL);
  55. Assert(_fDirty == FALSE);
  56. Assert(_nextOnChangeRangeInIcsub == NULL);
  57. if (ao == OWN_ANCHORS)
  58. {
  59. _paStart = paStart;
  60. _paEnd = paEnd;
  61. }
  62. else
  63. {
  64. Assert(ao == COPY_ANCHORS);
  65. if (paStart->Clone(&_paStart) != S_OK || _paStart == NULL)
  66. goto ErrorExit;
  67. if (paEnd->Clone(&_paEnd) != S_OK || _paEnd == NULL)
  68. goto ErrorExit;
  69. }
  70. _pic = pic;
  71. switch (rinit)
  72. {
  73. case RINIT_DEF_GRAVITY:
  74. Assert(CompareAnchors(paStart, paEnd) <= 0); // Issue: this is only a safe assert for acp implementations
  75. if (_SetGravity(TF_GRAVITY_BACKWARD, TF_GRAVITY_FORWARD, FALSE) != S_OK)
  76. goto ErrorExit;
  77. break;
  78. case RINIT_GRAVITY:
  79. if (_paStart->GetGravity(&gStart) != S_OK)
  80. goto ErrorExit;
  81. if (_paEnd->GetGravity(&gEnd) != S_OK)
  82. goto ErrorExit;
  83. _InitLastLockReleaseId(gStart, gEnd);
  84. break;
  85. default:
  86. // caller must init _dwLastLockReleaseID!
  87. break;
  88. }
  89. _pic->AddRef();
  90. return TRUE;
  91. ErrorExit:
  92. Assert(0);
  93. if (ao == COPY_ANCHORS)
  94. {
  95. SafeReleaseClear(_paStart);
  96. SafeReleaseClear(_paEnd);
  97. }
  98. return FALSE;
  99. }
  100. //+---------------------------------------------------------------------------
  101. //
  102. // dtor
  103. //
  104. //----------------------------------------------------------------------------
  105. CRange::~CRange()
  106. {
  107. _paStart->Release();
  108. _paEnd->Release();
  109. _pic->Release();
  110. Assert(_prgChangeSinks == NULL || _prgChangeSinks->Count() == 0); // all ITfRangeChangeSink's should have been unadvised
  111. delete _prgChangeSinks;
  112. }
  113. //+---------------------------------------------------------------------------
  114. //
  115. // IUnknown
  116. //
  117. //----------------------------------------------------------------------------
  118. STDAPI CRange::QueryInterface(REFIID riid, void **ppvObj)
  119. {
  120. CAnchorRef *par;
  121. if (&riid == &IID_PRIV_CRANGE ||
  122. IsEqualIID(riid, IID_PRIV_CRANGE))
  123. {
  124. *ppvObj = SAFECAST(this, CRange *);
  125. return S_OK; // No AddRef for IID_PRIV_CRANGE! this is a private IID....
  126. }
  127. *ppvObj = NULL;
  128. if (IsEqualIID(riid, IID_ITfRange) ||
  129. IsEqualIID(riid, IID_IUnknown))
  130. {
  131. *ppvObj = SAFECAST(this, ITfRangeAnchor *);
  132. }
  133. else if (IsEqualIID(riid, IID_ITfRangeACP))
  134. {
  135. if ((par = GetCAnchorRef_NA(_paStart)) != NULL) // just a test to see if we're wrapping
  136. {
  137. *ppvObj = SAFECAST(this, ITfRangeACP *);
  138. }
  139. }
  140. else if (IsEqualIID(riid, IID_ITfRangeAnchor))
  141. {
  142. if ((par = GetCAnchorRef_NA(_paStart)) == NULL) // just a test to see if we're wrapping
  143. {
  144. *ppvObj = SAFECAST(this, ITfRangeAnchor *);
  145. }
  146. }
  147. else if (IsEqualIID(riid, IID_ITfSource))
  148. {
  149. *ppvObj = SAFECAST(this, ITfSource *);
  150. }
  151. if (*ppvObj)
  152. {
  153. AddRef();
  154. return S_OK;
  155. }
  156. return E_NOINTERFACE;
  157. }
  158. STDAPI_(ULONG) CRange::AddRef()
  159. {
  160. return ++_cRef;
  161. }
  162. STDAPI_(ULONG) CRange::Release()
  163. {
  164. long cr;
  165. cr = --_cRef;
  166. Assert(cr >= 0);
  167. if (cr == 0)
  168. {
  169. delete this;
  170. }
  171. return cr;
  172. }
  173. //+---------------------------------------------------------------------------
  174. //
  175. // _IsValidEditCookie
  176. //
  177. //----------------------------------------------------------------------------
  178. BOOL CRange::_IsValidEditCookie(TfEditCookie ec, DWORD dwFlags)
  179. {
  180. // any time someone is about to access the doc, we also need
  181. // to verify the last app edit didn't cross this range's anchors
  182. _QuickCheckCrossedAnchors();
  183. return _pic->_IsValidEditCookie(ec, dwFlags);
  184. }
  185. //+---------------------------------------------------------------------------
  186. //
  187. // _CheckCrossedAnchors
  188. //
  189. //----------------------------------------------------------------------------
  190. void CRange::_CheckCrossedAnchors()
  191. {
  192. DWORD dw;
  193. Assert(_dwLastLockReleaseID != IGNORE_LAST_LOCKRELEASED); // use _QuickCheckCrossedAnchors first!
  194. #ifdef DEBUG
  195. // we shold only make is this far if this range has TF_GRAVITY_FORWARD,
  196. // TF_GRAVITY_BACKWARD otherwise we should never be able to get crossed
  197. // anchors.
  198. TsGravity gStart;
  199. TsGravity gEnd;
  200. _paStart->GetGravity(&gStart);
  201. _paEnd->GetGravity(&gEnd);
  202. Assert(gStart == TS_GR_FORWARD && gEnd == TS_GR_BACKWARD);
  203. #endif // DEBUG
  204. dw = _pic->_GetLastLockReleaseID();
  205. Assert(dw != IGNORE_LAST_LOCKRELEASED);
  206. if (_dwLastLockReleaseID == dw)
  207. return;
  208. _dwLastLockReleaseID = dw;
  209. if (CompareAnchors(_paStart, _paEnd) > 0)
  210. {
  211. // for crossed anchors, we always move the start anchor to the end pos -- ie, don't move
  212. _paStart->ShiftTo(_paEnd);
  213. }
  214. }
  215. //+---------------------------------------------------------------------------
  216. //
  217. // GetText
  218. //
  219. //----------------------------------------------------------------------------
  220. STDAPI CRange::GetText(TfEditCookie ec, DWORD dwFlags, WCHAR *pch, ULONG cchMax, ULONG *pcch)
  221. {
  222. HRESULT hr;
  223. BOOL fMove;
  224. Perf_IncCounter(PERF_RGETTEXT_COUNT);
  225. if (pcch == NULL)
  226. return E_INVALIDARG;
  227. *pcch = 0;
  228. if (dwFlags & ~(TF_TF_MOVESTART | TF_TF_IGNOREEND))
  229. return E_INVALIDARG;
  230. if (!_IsValidEditCookie(ec, TF_ES_READ))
  231. {
  232. Assert(0);
  233. return TF_E_NOLOCK;
  234. }
  235. fMove = (dwFlags & TF_TF_MOVESTART);
  236. hr = _pic->_ptsi->GetText(0, _paStart, (dwFlags & TF_TF_IGNOREEND) ? NULL : _paEnd, pch, cchMax, pcch, fMove);
  237. if (hr != S_OK)
  238. {
  239. hr = E_FAIL;
  240. }
  241. // don't let the start advance past the end
  242. if (fMove && CompareAnchors(_paStart, _paEnd) > 0)
  243. {
  244. _paEnd->ShiftTo(_paStart);
  245. }
  246. return hr;
  247. }
  248. //+---------------------------------------------------------------------------
  249. //
  250. // SetText
  251. //
  252. //----------------------------------------------------------------------------
  253. STDAPI CRange::SetText(TfEditCookie ec, DWORD dwFlags, const WCHAR *pchText, LONG cch)
  254. {
  255. CComposition *pComposition;
  256. HRESULT hr;
  257. BOOL fNewComposition;
  258. Perf_IncCounter(PERF_RSETTEXT_COUNT);
  259. if (pchText == NULL && cch != 0)
  260. return E_INVALIDARG;
  261. if ((dwFlags & ~TF_ST_CORRECTION) != 0)
  262. return E_INVALIDARG;
  263. if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
  264. {
  265. Assert(0);
  266. return TF_E_NOLOCK;
  267. }
  268. hr = _PreEditCompositionCheck(ec, &pComposition, &fNewComposition);
  269. if (hr != S_OK)
  270. return hr;
  271. if (cch < 0)
  272. {
  273. cch = wcslen(pchText);
  274. }
  275. #ifdef DEBUG
  276. for (LONG i=0; i<cch; i++)
  277. {
  278. Assert(pchText[i] != TF_CHAR_EMBEDDED); // illegal to insert TF_CHAR_EMBEDDED!
  279. Assert(pchText[i] != TS_CHAR_REGION); // illegal to insert TS_CHAR_REGION!
  280. }
  281. #endif
  282. //
  283. // set the text
  284. //
  285. hr = _pic->_ptsi->SetText(dwFlags, _paStart, _paEnd, pchText ? pchText : L"", cch);
  286. if (hr == S_OK)
  287. {
  288. _pic->_DoPostTextEditNotifications(pComposition, ec, dwFlags, cch, NULL, NULL, this);
  289. }
  290. // terminate the default composition, if there is one
  291. if (fNewComposition)
  292. {
  293. Assert(pComposition != NULL);
  294. pComposition->EndComposition(ec);
  295. pComposition->Release(); // don't need Release if !fNewComposition
  296. }
  297. return hr;
  298. }
  299. //+---------------------------------------------------------------------------
  300. //
  301. // GetEmbedded
  302. //
  303. //----------------------------------------------------------------------------
  304. STDAPI CRange::GetEmbedded(TfEditCookie ec, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
  305. {
  306. if (ppunk == NULL)
  307. return E_INVALIDARG;
  308. *ppunk = NULL;
  309. if (!_IsValidEditCookie(ec, TF_ES_READ))
  310. {
  311. Assert(0);
  312. return TF_E_NOLOCK;
  313. }
  314. return _pic->_ptsi->GetEmbedded(0, _paStart, rguidService, riid, ppunk);
  315. }
  316. //+---------------------------------------------------------------------------
  317. //
  318. // Clone
  319. //
  320. //----------------------------------------------------------------------------
  321. STDAPI CRange::InsertEmbedded(TfEditCookie ec, DWORD dwFlags, IDataObject *pDataObject)
  322. {
  323. CComposition *pComposition;
  324. BOOL fNewComposition;
  325. HRESULT hr;
  326. if ((dwFlags & ~TF_IE_CORRECTION) != 0)
  327. return E_INVALIDARG;
  328. if (pDataObject == NULL)
  329. return E_INVALIDARG;
  330. if (!_IsValidEditCookie(ec, TF_ES_READWRITE))
  331. {
  332. Assert(0);
  333. return TF_E_NOLOCK;
  334. }
  335. hr = _PreEditCompositionCheck(ec, &pComposition, &fNewComposition);
  336. if (hr != S_OK)
  337. return hr;
  338. hr = _pic->_ptsi->InsertEmbedded(dwFlags, _paStart, _paEnd, pDataObject);
  339. if (hr == S_OK)
  340. {
  341. _pic->_DoPostTextEditNotifications(pComposition, ec, dwFlags, 1, NULL, NULL, this);
  342. }
  343. // terminate the default composition, if there is one
  344. if (fNewComposition)
  345. {
  346. Assert(pComposition != NULL);
  347. pComposition->EndComposition(ec);
  348. pComposition->Release(); // don't need Release if !fNewComposition
  349. }
  350. return hr;
  351. }
  352. //+---------------------------------------------------------------------------
  353. //
  354. // GetFormattedText
  355. //
  356. //----------------------------------------------------------------------------
  357. STDAPI CRange::GetFormattedText(TfEditCookie ec, IDataObject **ppDataObject)
  358. {
  359. if (ppDataObject == NULL)
  360. return E_INVALIDARG;
  361. *ppDataObject = NULL;
  362. if (!_IsValidEditCookie(ec, TF_ES_READ))
  363. {
  364. Assert(0);
  365. return TF_E_NOLOCK;
  366. }
  367. return _pic->_ptsi->GetFormattedText(_paStart, _paEnd, ppDataObject);
  368. }
  369. //+---------------------------------------------------------------------------
  370. //
  371. // Clone
  372. //
  373. //----------------------------------------------------------------------------
  374. STDAPI CRange::Clone(ITfRange **ppClone)
  375. {
  376. if (ppClone == NULL)
  377. return E_INVALIDARG;
  378. return (*ppClone = (ITfRangeAnchor *)_Clone()) ? S_OK : E_OUTOFMEMORY;
  379. }
  380. //+---------------------------------------------------------------------------
  381. //
  382. // GetContext
  383. //
  384. //----------------------------------------------------------------------------
  385. STDAPI CRange::GetContext(ITfContext **ppContext)
  386. {
  387. if (ppContext == NULL)
  388. return E_INVALIDARG;
  389. *ppContext = _pic;
  390. if (*ppContext)
  391. {
  392. (*ppContext)->AddRef();
  393. return S_OK;
  394. }
  395. return E_FAIL;
  396. }
  397. //+---------------------------------------------------------------------------
  398. //
  399. // ShiftStart
  400. //
  401. //----------------------------------------------------------------------------
  402. STDAPI CRange::ShiftStart(TfEditCookie ec, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
  403. {
  404. CRange *pRangeP;
  405. IAnchor *paLimit;
  406. IAnchor *paShift;
  407. HRESULT hr;
  408. Perf_IncCounter(PERF_SHIFTSTART_COUNT);
  409. if (pcch == NULL)
  410. return E_INVALIDARG;
  411. *pcch = 0;
  412. if (pHalt != NULL && (pHalt->dwFlags & ~TF_HF_OBJECT))
  413. return E_INVALIDARG;
  414. if (!_IsValidEditCookie(ec, TF_ES_READ))
  415. {
  416. Assert(0);
  417. return TF_E_NOLOCK;
  418. }
  419. paLimit = NULL;
  420. if (pHalt != NULL && pHalt->pHaltRange != NULL)
  421. {
  422. if ((pRangeP = GetCRange_NA(pHalt->pHaltRange)) == NULL)
  423. return E_FAIL;
  424. paLimit = (pHalt->aHaltPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
  425. }
  426. if (pHalt == NULL || pHalt->dwFlags == 0)
  427. {
  428. // caller doesn't care about special chars, so we can do it the easy way
  429. hr = _paStart->Shift(0, cchReq, pcch, paLimit);
  430. }
  431. else
  432. {
  433. // caller wants us to halt for special chars, need to read text
  434. if (_paStart->Clone(&paShift) != S_OK)
  435. return E_FAIL;
  436. hr = _ShiftConditional(paShift, paLimit, cchReq, pcch, pHalt);
  437. if (hr == S_OK)
  438. {
  439. hr = _paStart->ShiftTo(paShift);
  440. }
  441. paShift->Release();
  442. }
  443. if (hr != S_OK)
  444. return E_FAIL;
  445. // don't let the start advance past the end
  446. if (cchReq > 0 && CompareAnchors(_paStart, _paEnd) > 0)
  447. {
  448. _paEnd->ShiftTo(_paStart);
  449. }
  450. return S_OK;
  451. }
  452. //+---------------------------------------------------------------------------
  453. //
  454. // ShiftEnd
  455. //
  456. //----------------------------------------------------------------------------
  457. STDAPI CRange::ShiftEnd(TfEditCookie ec, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
  458. {
  459. CRange *pRangeP;
  460. IAnchor *paLimit;
  461. IAnchor *paShift;
  462. HRESULT hr;
  463. Perf_IncCounter(PERF_SHIFTEND_COUNT);
  464. if (pcch == NULL)
  465. return E_INVALIDARG;
  466. *pcch = 0;
  467. if (pHalt != NULL && (pHalt->dwFlags & ~TF_HF_OBJECT))
  468. return E_INVALIDARG;
  469. if (!_IsValidEditCookie(ec, TF_ES_READ))
  470. {
  471. Assert(0);
  472. return TF_E_NOLOCK;
  473. }
  474. paLimit = NULL;
  475. if (pHalt != NULL && pHalt->pHaltRange != NULL)
  476. {
  477. if ((pRangeP = GetCRange_NA(pHalt->pHaltRange)) == NULL)
  478. return E_FAIL;
  479. paLimit = (pHalt->aHaltPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
  480. }
  481. if (pHalt == NULL || pHalt->dwFlags == 0)
  482. {
  483. // caller doesn't care about special chars, so we can do it the easy way
  484. hr = _paEnd->Shift(0, cchReq, pcch, paLimit);
  485. }
  486. else
  487. {
  488. // caller wants us to halt for special chars, need to read text
  489. if (_paEnd->Clone(&paShift) != S_OK)
  490. return E_FAIL;
  491. hr = _ShiftConditional(paShift, paLimit, cchReq, pcch, pHalt);
  492. if (hr == S_OK)
  493. {
  494. hr = _paEnd->ShiftTo(paShift);
  495. }
  496. paShift->Release();
  497. }
  498. if (hr != S_OK)
  499. return E_FAIL;
  500. // don't let the start advance past the end
  501. if (cchReq < 0 && CompareAnchors(_paStart, _paEnd) > 0)
  502. {
  503. _paStart->ShiftTo(_paEnd);
  504. }
  505. return S_OK;
  506. }
  507. //+---------------------------------------------------------------------------
  508. //
  509. // _ShiftConditional
  510. //
  511. //----------------------------------------------------------------------------
  512. HRESULT CRange::_ShiftConditional(IAnchor *paStart, IAnchor *paLimit, LONG cchReq, LONG *pcch, const TF_HALTCOND *pHalt)
  513. {
  514. HRESULT hr;
  515. ITextStoreAnchor *ptsi;
  516. LONG cchRead;
  517. LONG cch;
  518. LONG i;
  519. LONG iStop;
  520. LONG delta;
  521. BOOL fHaltObj;
  522. WCHAR ach[64];
  523. Assert(*pcch == 0);
  524. Assert(pHalt && pHalt->dwFlags);
  525. hr = S_OK;
  526. ptsi = _pic->_ptsi;
  527. fHaltObj = pHalt->dwFlags & TF_HF_OBJECT;
  528. delta = (cchReq > 0) ? +1 : -1;
  529. while (cchReq != 0)
  530. {
  531. if (cchReq > 0)
  532. {
  533. cch = (LONG)min(cchReq, ARRAYSIZE(ach));
  534. }
  535. else
  536. {
  537. // going backwards is tricky!
  538. cch = max(cchReq, -(LONG)ARRAYSIZE(ach));
  539. hr = paStart->Shift(0, cch, &cchRead, paLimit);
  540. if (hr != S_OK)
  541. break;
  542. if (cchRead == 0)
  543. break; // at top of doc or hit paLimit
  544. cch = -cchRead; // must read text forward
  545. }
  546. Perf_IncCounter(PERF_SHIFTCOND_GETTEXT);
  547. hr = ptsi->GetText(0, paStart, paLimit, ach, cch, (ULONG *)&cchRead, (cchReq > 0));
  548. if (hr != S_OK)
  549. break;
  550. if (cchRead == 0)
  551. break; // end of doc
  552. if (fHaltObj)
  553. {
  554. // scan for special chars
  555. if (cchReq > 0)
  556. {
  557. // scan left-to-right
  558. i = 0;
  559. iStop = cchRead;
  560. }
  561. else
  562. {
  563. // scan right-to-left
  564. i = cchRead - 1;
  565. iStop = -1;
  566. }
  567. for (; i != iStop; i += delta)
  568. {
  569. if (ach[i] == TS_CHAR_EMBEDDED)
  570. {
  571. if (cchReq > 0)
  572. {
  573. hr = paStart->Shift(0, i - cchRead, &cch, NULL);
  574. cchReq = cchRead = i;
  575. }
  576. else
  577. {
  578. hr = paStart->Shift(0, i + 1, &cch, NULL);
  579. cchRead -= i + 1;
  580. cchReq = -cchRead;
  581. }
  582. goto ExitLoop;
  583. }
  584. }
  585. }
  586. ExitLoop:
  587. if (cchReq < 0)
  588. {
  589. cchRead = -cchRead;
  590. }
  591. cchReq -= cchRead;
  592. *pcch += cchRead;
  593. }
  594. if (hr != S_OK)
  595. {
  596. *pcch = 0;
  597. }
  598. return hr;
  599. }
  600. //+---------------------------------------------------------------------------
  601. //
  602. // ShiftStartToRange
  603. //
  604. //----------------------------------------------------------------------------
  605. STDAPI CRange::ShiftStartToRange(TfEditCookie ec, ITfRange *pRange, TfAnchor aPos)
  606. {
  607. CRange *pRangeP;
  608. HRESULT hr;
  609. if (pRange == NULL)
  610. return E_INVALIDARG;
  611. if (!_IsValidEditCookie(ec, TF_ES_READ))
  612. {
  613. Assert(0);
  614. return TF_E_NOLOCK;
  615. }
  616. if ((pRangeP = GetCRange_NA(pRange)) == NULL)
  617. return E_INVALIDARG;
  618. if (!VerifySameContext(this, pRangeP))
  619. return E_INVALIDARG;
  620. pRangeP->_QuickCheckCrossedAnchors();
  621. hr = _paStart->ShiftTo((aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd());
  622. // don't let the start advance past the end
  623. if (CompareAnchors(_paStart, _paEnd) > 0)
  624. {
  625. _paEnd->ShiftTo(_paStart);
  626. }
  627. return hr;
  628. }
  629. //+---------------------------------------------------------------------------
  630. //
  631. // ShiftEndToRange
  632. //
  633. //----------------------------------------------------------------------------
  634. STDAPI CRange::ShiftEndToRange(TfEditCookie ec, ITfRange *pRange, TfAnchor aPos)
  635. {
  636. CRange *pRangeP;
  637. HRESULT hr;
  638. if (pRange == NULL)
  639. return E_INVALIDARG;
  640. if (!_IsValidEditCookie(ec, TF_ES_READ))
  641. {
  642. Assert(0);
  643. return TF_E_NOLOCK;
  644. }
  645. if ((pRangeP = GetCRange_NA(pRange)) == NULL)
  646. return E_FAIL;
  647. if (!VerifySameContext(this, pRangeP))
  648. return E_INVALIDARG;
  649. pRangeP->_QuickCheckCrossedAnchors();
  650. hr = _paEnd->ShiftTo((aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd());
  651. // don't let the end advance past the start
  652. if (CompareAnchors(_paStart, _paEnd) > 0)
  653. {
  654. _paStart->ShiftTo(_paEnd);
  655. }
  656. return hr;
  657. }
  658. //+---------------------------------------------------------------------------
  659. //
  660. // ShiftStartRegion
  661. //
  662. //----------------------------------------------------------------------------
  663. STDAPI CRange::ShiftStartRegion(TfEditCookie ec, TfShiftDir dir, BOOL *pfNoRegion)
  664. {
  665. HRESULT hr;
  666. if (pfNoRegion == NULL)
  667. return E_INVALIDARG;
  668. *pfNoRegion = TRUE;
  669. if (!_IsValidEditCookie(ec, TF_ES_READ))
  670. {
  671. Assert(0);
  672. return TF_E_NOLOCK;
  673. }
  674. hr = _paStart->ShiftRegion(0, (TsShiftDir)dir, pfNoRegion);
  675. if (hr == S_OK && dir == TF_SD_FORWARD && !*pfNoRegion)
  676. {
  677. // don't let the start advance past the end
  678. if (CompareAnchors(_paStart, _paEnd) > 0)
  679. {
  680. _paEnd->ShiftTo(_paStart);
  681. }
  682. }
  683. else if (hr == E_NOTIMPL)
  684. {
  685. // app doesn't support regions, so we can still succeed
  686. // it's just that there's no region to shift over
  687. *pfNoRegion = TRUE; // be paranoid, the app could be wacky
  688. hr = S_OK;
  689. }
  690. return hr;
  691. }
  692. //+---------------------------------------------------------------------------
  693. //
  694. // ShiftEndRegion
  695. //
  696. //----------------------------------------------------------------------------
  697. STDAPI CRange::ShiftEndRegion(TfEditCookie ec, TfShiftDir dir, BOOL *pfNoRegion)
  698. {
  699. HRESULT hr;
  700. if (pfNoRegion == NULL)
  701. return E_INVALIDARG;
  702. *pfNoRegion = TRUE;
  703. if (!_IsValidEditCookie(ec, TF_ES_READ))
  704. {
  705. Assert(0);
  706. return TF_E_NOLOCK;
  707. }
  708. hr = _paEnd->ShiftRegion(0, (TsShiftDir)dir, pfNoRegion);
  709. if (hr == S_OK && dir == TF_SD_BACKWARD && !*pfNoRegion)
  710. {
  711. // don't let the end advance past the start
  712. if (CompareAnchors(_paStart, _paEnd) > 0)
  713. {
  714. _paStart->ShiftTo(_paEnd);
  715. }
  716. }
  717. else if (hr == E_NOTIMPL)
  718. {
  719. // app doesn't support regions, so we can still succeed
  720. // it's just that there's no region to shift over
  721. *pfNoRegion = TRUE; // be paranoid, the app could be wacky
  722. hr = S_OK;
  723. }
  724. return hr;
  725. }
  726. //+---------------------------------------------------------------------------
  727. //
  728. // _SnapToRegion
  729. //
  730. //----------------------------------------------------------------------------
  731. #if 0
  732. HRESULT CRange::_SnapToRegion(DWORD dwFlags)
  733. {
  734. ITfRange *range;
  735. TF_HALTCOND hc;
  736. LONG cch;
  737. HRESULT hr;
  738. if (Clone(&range) != S_OK)
  739. return E_OUTOFMEMORY;
  740. hc.pHaltRange = (ITfRangeAnchor *)this;
  741. hc.dwFlags = 0;
  742. if (dwFlags & TF_GS_SNAPREGION_START)
  743. {
  744. if ((hr = range->Collapse(BACKDOOR_EDIT_COOKIE, TF_ANCHOR_START)) != S_OK)
  745. goto Exit;
  746. hc.aHaltPos = TF_ANCHOR_END;
  747. do
  748. {
  749. if ((hr = range->ShiftEnd(BACKDOOR_EDIT_COOKIE, LONG_MAX, &cch, &hc)) != S_OK)
  750. goto Exit;
  751. }
  752. while (cch >= LONG_MAX); // just in case this is a _really_ huge doc
  753. hr = ShiftEndToRange(BACKDOOR_EDIT_COOKIE, range, TF_ANCHOR_END);
  754. }
  755. else
  756. {
  757. Assert(dwFlags & TF_GS_SNAPREGION_END);
  758. if ((hr = range->Collapse(BACKDOOR_EDIT_COOKIE, TF_ANCHOR_END)) != S_OK)
  759. goto Exit;
  760. hc.aHaltPos = TF_ANCHOR_START;
  761. do
  762. {
  763. if ((hr = range->ShiftStart(BACKDOOR_EDIT_COOKIE, LONG_MIN, &cch, &hc)) != S_OK)
  764. goto Exit;
  765. }
  766. while (cch <= LONG_MIN); // just in case this is a _really_ huge doc
  767. hr = ShiftStartToRange(BACKDOOR_EDIT_COOKIE, range, TF_ANCHOR_START);
  768. }
  769. Exit:
  770. if (hr != S_OK)
  771. {
  772. hr = E_FAIL;
  773. }
  774. range->Release();
  775. return hr;
  776. }
  777. #endif // 0
  778. //+---------------------------------------------------------------------------
  779. //
  780. // IsEmpty
  781. //
  782. //----------------------------------------------------------------------------
  783. STDAPI CRange::IsEmpty(TfEditCookie ec, BOOL *pfEmpty)
  784. {
  785. return IsEqualStart(ec, (ITfRangeAnchor *)this, TF_ANCHOR_END, pfEmpty);
  786. }
  787. //+---------------------------------------------------------------------------
  788. //
  789. // Collapse
  790. //
  791. //----------------------------------------------------------------------------
  792. STDAPI CRange::Collapse(TfEditCookie ec, TfAnchor aPos)
  793. {
  794. if (!_IsValidEditCookie(ec, TF_ES_READ))
  795. {
  796. Assert(0);
  797. return TF_E_NOLOCK;
  798. }
  799. return (aPos == TF_ANCHOR_START) ? _paEnd->ShiftTo(_paStart) : _paStart->ShiftTo(_paEnd);
  800. }
  801. //+---------------------------------------------------------------------------
  802. //
  803. // IsEqualStart
  804. //
  805. //----------------------------------------------------------------------------
  806. STDAPI CRange::IsEqualStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
  807. {
  808. return _IsEqualX(ec, TF_ANCHOR_START, pWith, aPos, pfEqual);
  809. }
  810. //+---------------------------------------------------------------------------
  811. //
  812. // IsEqualEnd
  813. //
  814. //----------------------------------------------------------------------------
  815. STDAPI CRange::IsEqualEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
  816. {
  817. return _IsEqualX(ec, TF_ANCHOR_END, pWith, aPos, pfEqual);
  818. }
  819. //+---------------------------------------------------------------------------
  820. //
  821. // _IsEqualX
  822. //
  823. //----------------------------------------------------------------------------
  824. HRESULT CRange::_IsEqualX(TfEditCookie ec, TfAnchor aPosThisRange, ITfRange *pWith, TfAnchor aPos, BOOL *pfEqual)
  825. {
  826. LONG lComp;
  827. HRESULT hr;
  828. if (pfEqual == NULL)
  829. return E_INVALIDARG;
  830. *pfEqual = FALSE;
  831. // perf: we could check TS_SS_NOHIDDENTEXT for better perf
  832. hr = _CompareX(ec, aPosThisRange, pWith, aPos, &lComp);
  833. if (hr != S_OK)
  834. return hr;
  835. *pfEqual = (lComp == 0);
  836. return S_OK;
  837. }
  838. //+---------------------------------------------------------------------------
  839. //
  840. // CompareStart
  841. //
  842. //----------------------------------------------------------------------------
  843. STDAPI CRange::CompareStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
  844. {
  845. return _CompareX(ec, TF_ANCHOR_START, pWith, aPos, plResult);
  846. }
  847. //+---------------------------------------------------------------------------
  848. //
  849. // CompareEnd
  850. //
  851. //----------------------------------------------------------------------------
  852. STDAPI CRange::CompareEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
  853. {
  854. return _CompareX(ec, TF_ANCHOR_END, pWith, aPos, plResult);
  855. }
  856. //+---------------------------------------------------------------------------
  857. //
  858. // _CompareX
  859. //
  860. //----------------------------------------------------------------------------
  861. HRESULT CRange::_CompareX(TfEditCookie ec, TfAnchor aPosThisRange, ITfRange *pWith, TfAnchor aPos, LONG *plResult)
  862. {
  863. CRange *pRangeP;
  864. IAnchor *paThis;
  865. IAnchor *paWith;
  866. IAnchor *paTest;
  867. LONG lComp;
  868. LONG cch;
  869. BOOL fEqual;
  870. HRESULT hr;
  871. if (plResult == NULL)
  872. return E_INVALIDARG;
  873. *plResult = 0;
  874. if (!_IsValidEditCookie(ec, TF_ES_READ))
  875. {
  876. Assert(0);
  877. return TF_E_NOLOCK;
  878. }
  879. if (pWith == NULL)
  880. return E_INVALIDARG;
  881. if ((pRangeP = GetCRange_NA(pWith)) == NULL)
  882. return E_INVALIDARG;
  883. if (!VerifySameContext(this, pRangeP))
  884. return E_INVALIDARG;
  885. pRangeP->_QuickCheckCrossedAnchors();
  886. paWith = (aPos == TF_ANCHOR_START) ? pRangeP->_GetStart() : pRangeP->_GetEnd();
  887. paThis = (aPosThisRange == TF_ANCHOR_START) ? _paStart : _paEnd;
  888. if (paThis->Compare(paWith, &lComp) != S_OK)
  889. return E_FAIL;
  890. if (lComp == 0) // exact match
  891. {
  892. Assert(*plResult == 0);
  893. return S_OK;
  894. }
  895. // we need to account for hidden text, so we actually have to do a shift
  896. // perf: we could check TS_SS_NOHIDDENTEXT for better perf
  897. if (paThis->Shift(TS_SHIFT_COUNT_ONLY, (lComp < 0) ? 1 : -1, &cch, paWith) != S_OK)
  898. return E_FAIL;
  899. if (cch == 0)
  900. {
  901. // nothing but hidden text between the two anchors?
  902. // one special case: we might have hit a region boundary
  903. if (paThis->Clone(&paTest) != S_OK || paTest == NULL)
  904. return E_FAIL;
  905. hr = E_FAIL;
  906. // if we're not at paWith after the shift, we must have hit a region
  907. if (paTest->Shift(0, (lComp < 0) ? 1 : -1, &cch, paWith) != S_OK)
  908. goto ReleaseTest;
  909. Assert(cch == 0);
  910. if (paTest->IsEqual(paWith, &fEqual) != S_OK)
  911. goto ReleaseTest;
  912. hr = S_OK;
  913. ReleaseTest:
  914. paTest->Release();
  915. if (hr != S_OK)
  916. return E_FAIL;
  917. if (fEqual)
  918. {
  919. Assert(*plResult == 0);
  920. return S_OK;
  921. }
  922. }
  923. *plResult = lComp;
  924. return S_OK;
  925. }
  926. //+---------------------------------------------------------------------------
  927. //
  928. // GetGravity
  929. //
  930. //----------------------------------------------------------------------------
  931. STDAPI CRange::AdjustForInsert(TfEditCookie ec, ULONG cchInsert, BOOL *pfInsertOk)
  932. {
  933. TfGravity gStart;
  934. TfGravity gEnd;
  935. IAnchor *paStartResult;
  936. IAnchor *paEndResult;
  937. HRESULT hr;
  938. if (pfInsertOk == NULL)
  939. return E_INVALIDARG;
  940. *pfInsertOk = FALSE;
  941. if (!_IsValidEditCookie(ec, TF_ES_READ))
  942. {
  943. Assert(0);
  944. return TF_E_NOLOCK;
  945. }
  946. hr = _pic->_ptsi->QueryInsert(_paStart, _paEnd, cchInsert, &paStartResult, &paEndResult);
  947. if (hr == E_NOTIMPL)
  948. {
  949. // ok, just allow the request
  950. goto Exit;
  951. }
  952. else if (hr != S_OK)
  953. {
  954. Assert(*pfInsertOk == FALSE);
  955. return E_FAIL;
  956. }
  957. else if (paStartResult == NULL || paEndResult == NULL)
  958. {
  959. Assert(paEndResult == NULL);
  960. // NULL out params means no insert possible
  961. Assert(*pfInsertOk == FALSE);
  962. return S_OK;
  963. }
  964. // all set, just swap anchors and make sure gravity doesn't change
  965. GetGravity(&gStart, &gEnd);
  966. _paStart->Release();
  967. _paEnd->Release();
  968. _paStart = paStartResult;
  969. _paEnd = paEndResult;
  970. _SetGravity(gStart, gEnd, TRUE);
  971. Exit:
  972. *pfInsertOk = TRUE;
  973. return S_OK;
  974. }
  975. //+---------------------------------------------------------------------------
  976. //
  977. // GetGravity
  978. //
  979. //----------------------------------------------------------------------------
  980. STDAPI CRange::GetGravity(TfGravity *pgStart, TfGravity *pgEnd)
  981. {
  982. TsGravity gStart;
  983. TsGravity gEnd;
  984. if (pgStart == NULL || pgEnd == NULL)
  985. return E_INVALIDARG;
  986. _paStart->GetGravity(&gStart);
  987. _paEnd->GetGravity(&gEnd);
  988. *pgStart = (gStart == TS_GR_BACKWARD) ? TF_GRAVITY_BACKWARD : TF_GRAVITY_FORWARD;
  989. *pgEnd = (gEnd == TS_GR_BACKWARD) ? TF_GRAVITY_BACKWARD : TF_GRAVITY_FORWARD;
  990. return S_OK;
  991. }
  992. //+---------------------------------------------------------------------------
  993. //
  994. // SetGravity
  995. //
  996. //----------------------------------------------------------------------------
  997. STDAPI CRange::SetGravity(TfEditCookie ec, TfGravity gStart, TfGravity gEnd)
  998. {
  999. if (!_IsValidEditCookie(ec, TF_ES_READ))
  1000. {
  1001. Assert(0);
  1002. return TF_E_NOLOCK;
  1003. }
  1004. return _SetGravity(gStart, gEnd, TRUE);
  1005. }
  1006. //+---------------------------------------------------------------------------
  1007. //
  1008. // _SetGravity
  1009. //
  1010. //----------------------------------------------------------------------------
  1011. HRESULT CRange::_SetGravity(TfGravity gStart, TfGravity gEnd, BOOL fCheckCrossedAnchors)
  1012. {
  1013. if (fCheckCrossedAnchors)
  1014. {
  1015. // make sure we're not crossed in case we're switching away from inward gravity
  1016. _QuickCheckCrossedAnchors();
  1017. }
  1018. if (_paStart->SetGravity((TsGravity)gStart) != S_OK)
  1019. return E_FAIL;
  1020. if (_paEnd->SetGravity((TsGravity)gEnd) != S_OK)
  1021. return E_FAIL;
  1022. _InitLastLockReleaseId((TsGravity)gStart, (TsGravity)gEnd);
  1023. return S_OK;
  1024. }
  1025. //+---------------------------------------------------------------------------
  1026. //
  1027. // AdviseSink
  1028. //
  1029. //----------------------------------------------------------------------------
  1030. STDAPI CRange::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie)
  1031. {
  1032. const IID *rgiid = &IID_ITfRangeChangeSink;
  1033. HRESULT hr;
  1034. if (_prgChangeSinks == NULL)
  1035. {
  1036. // we delay allocate our sink container
  1037. if ((_prgChangeSinks = new CStructArray<GENERICSINK>) == NULL)
  1038. return E_OUTOFMEMORY;
  1039. }
  1040. hr = GenericAdviseSink(riid, punk, &rgiid, _prgChangeSinks, 1, pdwCookie);
  1041. if (hr == S_OK && _prgChangeSinks->Count() == 1)
  1042. {
  1043. // add this range to the list of ranges with sinks in the icsub
  1044. _nextOnChangeRangeInIcsub = _pic->_pOnChangeRanges;
  1045. _pic->_pOnChangeRanges = this;
  1046. // start tracking anchor collapses
  1047. //_paStart->TrackCollapse(TRUE);
  1048. //_paEnd->TrackCollapse(TRUE);
  1049. }
  1050. return hr;
  1051. }
  1052. //+---------------------------------------------------------------------------
  1053. //
  1054. // UnadviseSink
  1055. //
  1056. //----------------------------------------------------------------------------
  1057. STDAPI CRange::UnadviseSink(DWORD dwCookie)
  1058. {
  1059. CRange *pRange;
  1060. CRange **ppRange;
  1061. HRESULT hr;
  1062. if (_prgChangeSinks == NULL)
  1063. return CONNECT_E_NOCONNECTION;
  1064. hr = GenericUnadviseSink(_prgChangeSinks, 1, dwCookie);
  1065. if (hr == S_OK && _prgChangeSinks->Count() == 0)
  1066. {
  1067. // remove this range from the list of ranges in its icsub
  1068. ppRange = &_pic->_pOnChangeRanges;
  1069. while (pRange = *ppRange)
  1070. {
  1071. if (pRange == this)
  1072. {
  1073. *ppRange = pRange->_nextOnChangeRangeInIcsub;
  1074. break;
  1075. }
  1076. ppRange = &pRange->_nextOnChangeRangeInIcsub;
  1077. }
  1078. // stop tracking anchor collapses
  1079. //_paStart->TrackCollapse(FALSE);
  1080. //_paEnd->TrackCollapse(FALSE);
  1081. }
  1082. return hr;
  1083. }
  1084. //+---------------------------------------------------------------------------
  1085. //
  1086. // GetExtent
  1087. //
  1088. //----------------------------------------------------------------------------
  1089. STDAPI CRange::GetExtent(LONG *pacpAnchor, LONG *pcch)
  1090. {
  1091. CAnchorRef *par;
  1092. HRESULT hr = E_FAIL;
  1093. if (pacpAnchor == NULL || pcch == NULL)
  1094. return E_INVALIDARG;
  1095. *pacpAnchor = 0;
  1096. *pcch = 0;
  1097. // make the validation call anyways because we do other stuff in there
  1098. _IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
  1099. if ((par = GetCAnchorRef_NA(_paStart)) != NULL)
  1100. {
  1101. // we have a wrapped ACP impl, this is easy
  1102. *pacpAnchor = par->_GetACP();
  1103. if ((par = GetCAnchorRef_NA(_paEnd)) == NULL)
  1104. goto ErrorExit;
  1105. *pcch = par->_GetACP() - *pacpAnchor;
  1106. hr = S_OK;
  1107. }
  1108. else
  1109. {
  1110. Assert(0); // who's doing this?
  1111. // we fail if someone tries to do GetExtentACP on a
  1112. // non-acp text store. Users of this method should
  1113. // be aware of whether or not they are using an acp
  1114. // store.
  1115. }
  1116. return hr;
  1117. ErrorExit:
  1118. *pacpAnchor = 0;
  1119. *pcch = 0;
  1120. return E_FAIL;
  1121. }
  1122. //+---------------------------------------------------------------------------
  1123. //
  1124. // GetExtent
  1125. //
  1126. //----------------------------------------------------------------------------
  1127. STDAPI CRange::GetExtent(IAnchor **ppaStart, IAnchor **ppaEnd)
  1128. {
  1129. if (ppaStart == NULL || ppaEnd == NULL)
  1130. return E_INVALIDARG;
  1131. *ppaStart = NULL;
  1132. *ppaEnd = NULL;
  1133. // make the validation call anyways because we do other stuff in there
  1134. _IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
  1135. if (_paStart->Clone(ppaStart) != S_OK)
  1136. return E_FAIL;
  1137. if (_paEnd->Clone(ppaEnd) != S_OK)
  1138. {
  1139. SafeReleaseClear(*ppaStart);
  1140. return E_FAIL;
  1141. }
  1142. return S_OK;
  1143. }
  1144. //+---------------------------------------------------------------------------
  1145. //
  1146. // SetExtent
  1147. //
  1148. //----------------------------------------------------------------------------
  1149. STDAPI CRange::SetExtent(LONG acpAnchor, LONG cch)
  1150. {
  1151. CAnchorRef *par;
  1152. IAnchor *paStart;
  1153. IAnchor *paEnd;
  1154. // make the validation call anyways because we do other stuff in there
  1155. _IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
  1156. if (acpAnchor < 0 || cch < 0)
  1157. return E_INVALIDARG;
  1158. paStart = paEnd = NULL;
  1159. if ((par = GetCAnchorRef_NA(_paStart)) != NULL)
  1160. {
  1161. // we have a wrapped ACP impl, this is easy
  1162. // need to work with Clones to handle failure gracefully
  1163. if (FAILED(_paStart->Clone(&paStart)))
  1164. goto ErrorExit;
  1165. if ((par = GetCAnchorRef_NA(paStart)) == NULL)
  1166. goto ErrorExit;
  1167. if (!par->_SetACP(acpAnchor))
  1168. goto ErrorExit;
  1169. if (FAILED(_paEnd->Clone(&paEnd)))
  1170. goto ErrorExit;
  1171. if ((par = GetCAnchorRef_NA(paEnd)) == NULL)
  1172. goto ErrorExit;
  1173. if (!par->_SetACP(acpAnchor + cch))
  1174. goto ErrorExit;
  1175. }
  1176. else
  1177. {
  1178. Assert(0); // who's doing this?
  1179. // we fail if someone tries to do SetExtentACP on a
  1180. // non-acp text store. Users of this method should
  1181. // be aware of whether or not they are using an acp
  1182. // store.
  1183. goto ErrorExit;
  1184. }
  1185. SafeRelease(_paStart);
  1186. SafeRelease(_paEnd);
  1187. _paStart = paStart;
  1188. _paEnd = paEnd;
  1189. return S_OK;
  1190. ErrorExit:
  1191. SafeRelease(paStart);
  1192. SafeRelease(paEnd);
  1193. return E_FAIL;
  1194. }
  1195. //+---------------------------------------------------------------------------
  1196. //
  1197. // SetExtent
  1198. //
  1199. //----------------------------------------------------------------------------
  1200. STDAPI CRange::SetExtent(IAnchor *paStart, IAnchor *paEnd)
  1201. {
  1202. IAnchor *paStartClone;
  1203. IAnchor *paEndClone;
  1204. // make the validation call anyways because we do other stuff in there
  1205. _IsValidEditCookie(BACKDOOR_EDIT_COOKIE, TF_ES_READ);
  1206. if (paStart == NULL || paEnd == NULL)
  1207. return E_INVALIDARG;
  1208. if (CompareAnchors(paStart, paEnd) > 0)
  1209. return E_INVALIDARG;
  1210. if (paStart->Clone(&paStartClone) != S_OK)
  1211. return E_FAIL;
  1212. if (paEnd->Clone(&paEndClone) != S_OK)
  1213. {
  1214. paStartClone->Release();
  1215. return E_FAIL;
  1216. }
  1217. SafeRelease(_paStart);
  1218. SafeRelease(_paEnd);
  1219. _paStart = paStartClone;
  1220. _paEnd = paEndClone;
  1221. return S_OK;
  1222. }
  1223. //+---------------------------------------------------------------------------
  1224. //
  1225. // _PreEditCompositionCheck
  1226. //
  1227. //----------------------------------------------------------------------------
  1228. HRESULT CRange::_PreEditCompositionCheck(TfEditCookie ec, CComposition **ppComposition, BOOL *pfNewComposition)
  1229. {
  1230. IRC irc;
  1231. // any active compositions?
  1232. *pfNewComposition = FALSE;
  1233. irc = CComposition::_IsRangeCovered(_pic, _pic->_GetClientInEditSession(ec), _paStart, _paEnd, ppComposition);
  1234. if (irc == IRC_COVERED)
  1235. {
  1236. // this range is within an owned composition
  1237. Assert(*ppComposition != NULL);
  1238. return S_OK;
  1239. }
  1240. else if (irc == IRC_OUTSIDE)
  1241. {
  1242. // the caller owns compositions, but this range isn't wholly within them
  1243. return TF_E_RANGE_NOT_COVERED;
  1244. }
  1245. else
  1246. {
  1247. Assert(irc == IRC_NO_OWNEDCOMPOSITIONS);
  1248. }
  1249. // not covered, need to create a default composition
  1250. if (_pic->_StartComposition(ec, _paStart, _paEnd, NULL, ppComposition) != S_OK)
  1251. return E_FAIL;
  1252. if (*ppComposition != NULL)
  1253. {
  1254. *pfNewComposition = TRUE;
  1255. return S_OK;
  1256. }
  1257. return TF_E_COMPOSITION_REJECTED;
  1258. }