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.

3210 lines
106 KiB

  1. /*
  2. * @doc TOM
  3. *
  4. * @module TOMRANGE.CPP - Implement the CTxtRange Class |
  5. *
  6. * This module contains the implementation of the TOM ITextRange
  7. * interface on the CTxtRange object
  8. *
  9. * History: <nl>
  10. * 5/24/95 - Alex Gounares: stubs created <nl>
  11. * 8/95 - MurrayS: main implementation <nl>
  12. * 11/95 - MurrayS: upgrade to TOM spec of 12/10/95 <nl>
  13. * 5/96 - MurrayS: added zombie protection
  14. *
  15. * @comm
  16. * All ITextRange methods return HRESULTs. If the method can move a
  17. * range cp, the HRESULT is NOERROR if movement occurs and S_FALSE if
  18. * no movement occurs. These methods usually take a <p pDelta> argument
  19. * that returns the count of characters or Units actually moved. If this
  20. * parameter is NULL, E_INVALIDARG is returned. Other return values
  21. * include E_NOTIMPL, e.g., for Unit values not implemented,
  22. * E_OUTOFMEMORY, e.g., when allocations fail, and CO_E_RELEASED, when
  23. * the CTxtEdit (_ped) to which the range is attached has been deleted.
  24. *
  25. * For more complete documentation, please see tom.doc
  26. *
  27. * @devnote
  28. * All ptr parameters must be validated before use and all entry points
  29. * need to check whether this range is a zombie. These checks are
  30. * done in one of three places: 1) immediately on entry to a function,
  31. * 2) immediately on entry to a helper function (e.g., private Mover()
  32. * for the move methods), or 3) before storing the out value.
  33. * Alternative 3) is used for optional return values, such as pDelta
  34. * and pB.
  35. *
  36. * To achieve a simple, efficient inheritance model, CTxtSelection
  37. * inherits ITextSelection through CTxtRange. Otherwise we'd have a
  38. * diamond inheritance, since ITextSelection itself inherits from
  39. * ITextRange. Diamond inheritance creates two copies of the multiply
  40. * inherited class unless that class is inherited virtually. Virtual
  41. * inheritance uses run-time base-offset tables and is slower and
  42. * bigger. To avoid such a mess, we include the extra ITextSelection
  43. * methods in CTxtRange, with the intention that they'll never be called
  44. * and therefore they return E_NOTIMPL. This is overridden for
  45. * ITextSelection objects
  46. *
  47. * @future
  48. * 1) Finder match ^p, etc.
  49. * 2) Fast GetEffects() method. Would speed up the myriad IsProtected()
  50. * calls and be useful for getting other effects as well.
  51. * 3) Fast copies/pastes of RichEdit binary format. This can be done by
  52. * creating a method to copy a range to a new CTxtStory and a method
  53. * to insert a CTxtStory.
  54. * 4) Delayed rendering
  55. *
  56. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  57. */
  58. #include "_common.h"
  59. #include "_select.h"
  60. #include "_edit.h"
  61. #include "_line.h"
  62. #include "_frunptr.h"
  63. #include "_tomfmt.h"
  64. #include "_disp.h"
  65. #include "_objmgr.h"
  66. #include "_callmgr.h"
  67. #include "_measure.h"
  68. ASSERTDATA
  69. #define DEBUG_CLASSNAME CTxtRange
  70. #include "_invar.h"
  71. HRESULT QueryInterface (REFIID riid, REFIID riid1, IUnknown *punk,
  72. void **ppv, BOOL fZombie);
  73. //----------------- CTxtRange (ITextRange) PUBLIC methods ----------------------------------
  74. //----------------------- CTxtRange IUnknown Methods -------------------------------------
  75. /*
  76. * CTxtRange::QueryInterface (riid, ppv)
  77. *
  78. * @mfunc
  79. * IUnknown method
  80. *
  81. * @rdesc
  82. * HRESULT = (!ppv) ? E_INVALIDARG :
  83. * (interface found) ? NOERROR : E_NOINTERFACE
  84. */
  85. STDMETHODIMP CTxtRange::QueryInterface (
  86. REFIID riid, //@parm Reference to requested interface ID
  87. void ** ppv) //@parm Out parm to receive interface ptr
  88. {
  89. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::QueryInterface");
  90. REFIID riid1 = _fSel && IsEqualIID(riid, IID_ITextSelection)
  91. ? IID_ITextSelection : IID_ITextRange;
  92. #ifndef PEGASUS
  93. return ::QueryInterface(riid, riid1, this, ppv, IsZombie());
  94. #else
  95. return 0;
  96. #endif
  97. }
  98. /*
  99. * CTxtRange::AddRef()
  100. *
  101. * @mfunc
  102. * IUnknown method
  103. *
  104. * @rdesc
  105. * ULONG - incremented reference count
  106. */
  107. STDMETHODIMP_(ULONG) CTxtRange::AddRef()
  108. {
  109. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::AddRef");
  110. return ++_cRefs;
  111. }
  112. /*
  113. * CTxtRange::Release()
  114. *
  115. * @mfunc
  116. * IUnknown method
  117. *
  118. * @rdesc
  119. * ULONG - decremented reference count
  120. */
  121. STDMETHODIMP_(ULONG) CTxtRange::Release()
  122. {
  123. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Release");
  124. _cRefs--;
  125. if(!_cRefs)
  126. {
  127. delete this;
  128. return 0;
  129. }
  130. Assert(_cRefs > 0);
  131. return _cRefs;
  132. }
  133. //------------------------ CTxtRange IDispatch Methods -------------------------------------
  134. /*
  135. * CTxtRange::GetTypeInfoCount(pcTypeInfo)
  136. *
  137. * @mfunc
  138. * Get the number of TYPEINFO elements (1)
  139. *
  140. * @rdesc
  141. * HRESULT
  142. */
  143. STDMETHODIMP CTxtRange::GetTypeInfoCount (
  144. UINT * pcTypeInfo) //@parm Out parm to receive type-info count
  145. {
  146. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetTypeInfoCount");
  147. if(!pcTypeInfo)
  148. return E_INVALIDARG;
  149. *pcTypeInfo = 1;
  150. return NOERROR;
  151. }
  152. /*
  153. * CTxtRange::GetTypeInfo(iTypeInfo, lcid, ppTypeInfo)
  154. *
  155. * @mfunc
  156. * Return ptr to type information object for ITextSelection interface
  157. *
  158. * @rdesc
  159. * HRESULT
  160. */
  161. STDMETHODIMP CTxtRange::GetTypeInfo (
  162. UINT iTypeInfo, //@parm Index of type info to return
  163. LCID lcid, //@parm Local ID of type info
  164. ITypeInfo **ppTypeInfo) //@parm Out parm to receive type info
  165. {
  166. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetTypeInfo");
  167. return ::GetTypeInfo(iTypeInfo, g_pTypeInfoSel, ppTypeInfo);
  168. }
  169. /*
  170. * CTxtRange::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
  171. *
  172. * @mfunc
  173. * Get DISPIDs for methods in the ITextSelection, ITextRange, ITextFont,
  174. * and ITextPara interfaces
  175. *
  176. * @rdesc
  177. * HRESULT
  178. *
  179. * @devnote
  180. * If the ITextFont and ITextPara ever offer more methods than exposed
  181. * in their type libraries, the code should delegate to the corresponding
  182. * GetIDsOfNames. The current code only gets DISPIDs for the methods in
  183. * type libraries, thereby not having to instantiate the objects.
  184. */
  185. STDMETHODIMP CTxtRange::GetIDsOfNames (
  186. REFIID riid, //@parm Interface ID to interpret names for
  187. OLECHAR ** rgszNames, //@parm Array of names to be mapped
  188. UINT cNames, //@parm Count of names to be mapped
  189. LCID lcid, //@parm Local ID to use for interpretation
  190. DISPID * rgdispid) //@parm Out parm to receive name mappings
  191. {
  192. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetIDsOfNames");
  193. HRESULT hr = GetTypeInfoPtrs(); // Ensure TypeInfo ptrs are OK
  194. if(hr != NOERROR)
  195. return hr;
  196. if(g_pTypeInfoSel->GetIDsOfNames(rgszNames, cNames, rgdispid) == NOERROR)
  197. return NOERROR;
  198. if(g_pTypeInfoFont->GetIDsOfNames(rgszNames, cNames, rgdispid) == NOERROR)
  199. return NOERROR;
  200. return g_pTypeInfoPara->GetIDsOfNames(rgszNames, cNames, rgdispid);
  201. }
  202. /*
  203. * CTxtRange::Invoke(dispidMember, riid, lcid, wFlags, pdispparams,
  204. * pvarResult, pexcepinfo, puArgError)
  205. * @mfunc
  206. * Invoke methods for the ITextRange and ITextSelection objects, as
  207. * well as for ITextFont and ITextPara interfaces on those objects.
  208. *
  209. * @rdesc
  210. * HRESULT
  211. */
  212. STDMETHODIMP CTxtRange::Invoke (
  213. DISPID dispidMember, //@parm Identifies member function
  214. REFIID riid, //@parm Pointer to interface ID
  215. LCID lcid, //@parm Locale ID for interpretation
  216. USHORT wFlags, //@parm Flags describing context of call
  217. DISPPARAMS *pdispparams, //@parm Ptr to method arguments
  218. VARIANT * pvarResult, //@parm Out parm for result (if not NULL)
  219. EXCEPINFO * pexcepinfo, //@parm Out parm for exception info
  220. UINT * puArgError) //@parm Out parm for error
  221. {
  222. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Invoke");
  223. HRESULT hr = GetTypeInfoPtrs(); // Ensure TypeInfo ptrs are OK
  224. if(hr != NOERROR)
  225. return hr;
  226. if(IsZombie())
  227. return CO_E_RELEASED;
  228. IDispatch * pDispatch;
  229. ITypeInfo * pTypeInfo;
  230. if((DWORD)dispidMember <= 0x2ff) // Include default (0), selection,
  231. { // and range DISPIDs
  232. pTypeInfo = g_pTypeInfoSel;
  233. pDispatch = this;
  234. AddRef(); // Compensate for Release() below
  235. }
  236. else if((DWORD)dispidMember <= 0x3ff) // 0x300 to 0x3ff: DISPIDs
  237. { // reserved for ITextFont
  238. pTypeInfo = g_pTypeInfoFont;
  239. hr = GetFont((ITextFont**)&pDispatch);
  240. }
  241. else if((DWORD)dispidMember <= 0x4ff) // 0x400 to 0x4ff: DISPIDs
  242. { // reserved for ITextPara
  243. pTypeInfo = g_pTypeInfoPara;
  244. hr = GetPara((ITextPara **)&pDispatch);
  245. }
  246. else // dispidMember is negative or
  247. return DISP_E_MEMBERNOTFOUND; // > 0x4ff, i.e., not TOM
  248. if(hr != NOERROR) // Couldn't instantiate ITextFont
  249. return hr; // or ITextPara
  250. hr = pTypeInfo->Invoke(pDispatch, dispidMember, wFlags,
  251. pdispparams, pvarResult, pexcepinfo, puArgError);
  252. #ifndef PEGASUS
  253. pDispatch->Release();
  254. #endif
  255. return hr;
  256. }
  257. //----------------------- ITextRange Methods/Properties ------------------------
  258. /*
  259. * CTxtRange::CanEdit (pB)
  260. *
  261. * @mfunc
  262. * Set *<p pB> = tomTrue iff this range can be edited and
  263. * pB isn't NULL
  264. *
  265. * @rdesc
  266. * HRESULT = (can edit) ? NOERROR : S_FALSE
  267. */
  268. STDMETHODIMP CTxtRange::CanEdit (
  269. long * pB) //@parm Out parm to receive boolean value
  270. {
  271. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::CanEdit");
  272. if(IsZombie())
  273. return CO_E_RELEASED;
  274. CCallMgr callmgr(GetPed());
  275. return IsTrue(!WriteAccessDenied(), pB);
  276. }
  277. /*
  278. * CTxtRange::CanPaste (pVar, long Format, pB)
  279. *
  280. * @mfunc
  281. * Set *<p pB> = tomTrue iff the data object <p pVar>->punkVal can be
  282. * pasted into this range and pB isn't NULL. If <p pVar> is NULL,
  283. * use the clipboard instead.
  284. *
  285. * @rdesc
  286. * HRESULT = (can paste) ? NOERROR : S_FALSE
  287. */
  288. STDMETHODIMP CTxtRange::CanPaste (
  289. VARIANT * pVar, //@parm Data object to paste
  290. long Format, //@parm Desired clipboard format
  291. long * pB) //@parm Out parm to receive boolean value
  292. {
  293. #ifndef PEGASUS
  294. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::CanPaste");
  295. if(IsZombie())
  296. return CO_E_RELEASED;
  297. CCallMgr callmgr(GetPed());
  298. HRESULT hr;
  299. IDataObject * pdo = NULL; // Default clipboard
  300. if(pVar && pVar->vt == VT_UNKNOWN)
  301. pVar->punkVal->QueryInterface(IID_IDataObject, (void **)&pdo);
  302. hr = IsTrue(!WriteAccessDenied() &&
  303. (GetPed()->GetDTE()->CanPaste(pdo, (CLIPFORMAT)Format,
  304. RECO_PASTE)), pB);
  305. if(pdo)
  306. pdo->Release();
  307. return hr;
  308. #else
  309. return 0;
  310. #endif
  311. }
  312. /*
  313. * ITextRange::ChangeCase (long Type)
  314. *
  315. * @mfunc
  316. * Change the case of letters in this range according to Type:
  317. *
  318. * tomSentenceCase = 0: capitalize first letter of each sentence
  319. * tomLowerCase = 1: change all letters to lower case
  320. * tomUpperCase = 2: change all letters to upper case
  321. * tomTitleCase = 3: capitalize the first letter of each word
  322. * tomToggleCase = 4: toggle the case of each letter
  323. *
  324. * @rdesc
  325. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  326. * (if change) ? NOERROR : S_FALSE
  327. */
  328. STDMETHODIMP CTxtRange::ChangeCase (
  329. long Type) //@parm Type of case change. Default value: tomLower
  330. {
  331. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::ChangeCase");
  332. if(IsZombie())
  333. return CO_E_RELEASED;
  334. CCallMgr callmgr(GetPed());
  335. if(WriteAccessDenied())
  336. return E_ACCESSDENIED;
  337. IUndoBuilder * publdr;
  338. CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
  339. LONG cpMin, cpMax;
  340. LONG cch = GetRange(cpMin, cpMax);
  341. CRchTxtPtr rtp(*this);
  342. undobldr.StopGroupTyping();
  343. rtp.SetCp(cpMin);
  344. return (rtp.ChangeCase(cch, Type, publdr)) ? NOERROR : S_FALSE;
  345. }
  346. /*
  347. * CTxtRange::Collapse (bStart)
  348. *
  349. * @mfunc
  350. * Collapse this range into a degenerate point either at the
  351. * the start (<p bStart> is nonzero or the end (<p bStart> = 0)
  352. *
  353. * @rdesc
  354. * HRESULT = (if change) ? NOERROR : S_FALSE
  355. */
  356. STDMETHODIMP CTxtRange::Collapse (
  357. long bStart) //@parm Flag specifying end to collapse at
  358. {
  359. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Collapse");
  360. if(IsZombie())
  361. return CO_E_RELEASED;
  362. CCallMgr callmgr(GetPed());
  363. if(!_cch) // Already collapsed
  364. return S_FALSE; // Signal that no change occurred
  365. Collapser(bStart);
  366. Update(TRUE); // Update selection
  367. return NOERROR; // Signal that change occurred
  368. }
  369. /*
  370. * CTxtRange::Copy (pVar)
  371. *
  372. * @mfunc
  373. * Copy the plain and/or rich text to a data object and return the
  374. * object ptr in <p pVar>. If <p pVar> is null, copy to the clipboard.
  375. *
  376. * @rdesc
  377. * HRESULT = (if success) ? NOERROR : E_OUTOFMEMORY
  378. */
  379. STDMETHODIMP CTxtRange::Copy (
  380. VARIANT * pVar) //@parm Out parm for data object
  381. {
  382. #ifndef PEGASUS
  383. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Copy");
  384. if(IsZombie())
  385. return CO_E_RELEASED;
  386. CLightDTEngine * pldte = &GetPed()->_ldte;
  387. if(pVar && pVar->vt == (VT_UNKNOWN | VT_BYREF))
  388. {
  389. return pldte->RangeToDataObject(this, SF_TEXT | SF_RTF,
  390. (IDataObject **)pVar->ppunkVal);
  391. }
  392. return pldte->CopyRangeToClipboard(this);
  393. #else
  394. return 0;
  395. #endif
  396. }
  397. /*
  398. * CTxtRange::Cut (pVar)
  399. *
  400. * @mfunc
  401. * Cut the plain and/or rich text to a data object and return the
  402. * object ptr in <p pVar>. If <p pVar> is null,
  403. * cut to the clipboard.
  404. *
  405. * @rdesc
  406. * HRESULT = (if success) ? NOERROR : E_OUTOFMEMORY
  407. */
  408. STDMETHODIMP CTxtRange::Cut (
  409. VARIANT * pVar) //@parm Out parm for data object
  410. {
  411. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Cut");
  412. if(IsZombie())
  413. return CO_E_RELEASED;
  414. CCallMgr callmgr(GetPed());
  415. if(WriteAccessDenied())
  416. return E_ACCESSDENIED;
  417. HRESULT hr = Copy(pVar);
  418. Replacer(0, NULL);
  419. return hr;
  420. }
  421. /*
  422. * CTxtRange::Delete (Unit, Count, pDelta)
  423. *
  424. * @mfunc
  425. * If this range is nondegenerate, delete it along with |Count| - 1 Units
  426. * in the direction specified by the sign of Count. If this range is
  427. * degenerate, delete Count Units.
  428. *
  429. * @rdesc
  430. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  431. * (all requested Units deleted) ? NOERROR : S_FALSE
  432. */
  433. STDMETHODIMP CTxtRange::Delete (
  434. long Unit, //@parm Unit to use
  435. long Count, //@parm Number of chars to delete
  436. long * pDelta) //@parm Out parm to receive count of units deleted
  437. {
  438. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Delete");
  439. if(pDelta)
  440. *pDelta = 0;
  441. if(IsZombie())
  442. return CO_E_RELEASED;
  443. CCallMgr callmgr(GetPed());
  444. if(WriteAccessDenied())
  445. return E_ACCESSDENIED;
  446. LONG cchSave = _cch; // Remember initial count
  447. LONG cchText = GetAdjustedTextLength();
  448. LONG CountOrg = Count;
  449. LONG cpMin, cpMost;
  450. LONG cUnit = 0; // Default no Units
  451. MOVES Mode = (Count >= 0) ? MOVE_END : MOVE_START;
  452. GetRange(cpMin, cpMost);
  453. if(cpMost > cchText) // Can't delete final CR. To get
  454. { // *pDelta right, handle here
  455. Set(cpMin, cpMin - cchText); // Backup before CR & set active
  456. if(Count > 0) // end at cpMin since there's
  457. { // nothing to delete forward
  458. Count = 0;
  459. if(!_cch) // Only collapsed selection of
  460. Mode = MOVE_IP; // final CR: set up nothing
  461. } // deleted (MOVE_IP = 0)
  462. }
  463. if(Count)
  464. {
  465. if((_cch ^ Mode) < 0) // Be sure active end is in
  466. FlipRange(); // deletion direction
  467. if(cchSave) // Deleting nondegenerate range
  468. Count -= Mode; // counts as one unit
  469. if(Mover(Unit, Count, &cUnit, Mode) // Try to expand range for
  470. == E_INVALIDARG) // remaining Count Units
  471. {
  472. if(pDelta)
  473. *pDelta = 0;
  474. return E_INVALIDARG;
  475. }
  476. if(GetCp() > cchText && cUnit > 0) // Range includes final CR, which
  477. { // cannot be deleted. Reduce
  478. if(Unit == tomCharacter) // counts for some Units
  479. cUnit -= GetTextLength() - cchText;
  480. else if(Unit == tomWord)
  481. cUnit--; // An EOP qualifies as a tomWord
  482. }
  483. }
  484. if(cchSave) // Deleting nondegenerate range
  485. cUnit += Mode; // counts as a Unit
  486. if(pDelta)
  487. *pDelta = cUnit;
  488. if(_cch) // Mover() may have changed _cch
  489. {
  490. IUndoBuilder * publdr;
  491. CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
  492. if (publdr)
  493. {
  494. publdr->StopGroupTyping();
  495. publdr->SetNameID(UID_DELETE);
  496. }
  497. // FUTURE (murrays): the cchSave case should set up undo to
  498. // restore the original range, not the extended range resulting
  499. // when |CountOrg| > 1. This could be done using two calls to
  500. // ReplaceRange(), one to delete the original range and one to
  501. // delete the rest
  502. SELRR selrr = !_fSel || cchSave ? SELRR_REMEMBERRANGE :
  503. CountOrg > 0 ? SELRR_REMEMBERCPMIN :
  504. SELRR_REMEMBERENDIP;
  505. ReplaceRange(0, NULL, publdr, selrr);
  506. if (cUnit == CountOrg || // Delete(Unit,0,0)
  507. cUnit == 1 && !CountOrg) // deletes one "Unit", namely
  508. { // what's selected
  509. return NOERROR; // Signal everything deleted as
  510. } // requested
  511. }
  512. else if(cchSave) // Collapsed selection of final CR
  513. { // but didn't delete anything
  514. Update(TRUE); // Selection highlighting changed
  515. }
  516. return S_FALSE; // Less deleted than requested
  517. }
  518. /*
  519. * CTxtRange::EndOf (Unit, Extend, pDelta)
  520. *
  521. * @mfunc
  522. * Move this range end(s) to end of the first overlapping Unit in
  523. * the range.
  524. *
  525. * @rdesc
  526. * HRESULT = (if change) ? NOERROR :
  527. * (if Unit supported) ? S_FALSE : E_NOTIMPL
  528. */
  529. STDMETHODIMP CTxtRange::EndOf (
  530. long Unit, //@parm Unit to use
  531. long Extend, //@parm If true, leave other end alone
  532. long * pDelta) //@parm Count of chars that End is moved
  533. {
  534. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::EndOf");
  535. CCallMgr callmgr(GetPed());
  536. LONG cpMost;
  537. HRESULT hr = Expander (Unit, Extend, pDelta, NULL, &cpMost);
  538. if(hr == NOERROR)
  539. Update(TRUE); // Update selection
  540. return hr;
  541. }
  542. /*
  543. * CTxtRange::Expand (Unit, pDelta)
  544. *
  545. * @mfunc
  546. * Expand this range so that any partial Units it contains are
  547. * completely contained. If this range consists of one or more full
  548. * Units, no change is made. If this range is an insertion point at
  549. * the beginning or within a Unit, Expand() expands this range to include
  550. * that Unit. If this range is an insertion point at the end of the
  551. * story, Expand() tries to set this range to include the last Unit in
  552. * the story. The active end is always cpMost except for the last case.
  553. *
  554. * @rdesc
  555. * HRESULT = (if change) ? NOERROR :
  556. * (if Unit supported) ? S_FALSE : E_NOTIMPL
  557. */
  558. STDMETHODIMP CTxtRange::Expand (
  559. long Unit, //@parm Unit to expand range to
  560. long * pDelta) //@parm Out parm to receive count of chars added
  561. {
  562. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Expand");
  563. CCallMgr callmgr(GetPed());
  564. LONG cpMin, cpMost;
  565. HRESULT hr = Expander (Unit, TRUE, pDelta, &cpMin, &cpMost);
  566. if(hr == NOERROR)
  567. Update(TRUE); // Update selection
  568. return hr;
  569. }
  570. /*
  571. * CTxtRange::FindText (bstr, cch, Flags, pLength)
  572. *
  573. * @mfunc
  574. * If this range isn't an insertion point already, convert it into an
  575. * insertion point at its End if <p cch> <gt> 0 and at its Start if
  576. * <p cch> <lt> 0. Then search up to <p cch> characters of the range
  577. * looking for the string <p bstr> subject to the compare flags
  578. * <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
  579. * <lt> 0 the search is backward. If the string is found, the range
  580. * limits are changed to be those of the matched string and *<p pLength>
  581. * is set equal to the length of the string. If the string isn't found,
  582. * the range remains unchanged and *<p pLength> is set equal to 0.
  583. *
  584. * @rdesc
  585. * HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
  586. *
  587. * @devnote
  588. * Argument validation of the three Find methods is done by the helper
  589. * function CTxtRange::Finder(bstr, cch, dwFlags, pDelta, fExtend, fFlip)
  590. */
  591. STDMETHODIMP CTxtRange::FindText (
  592. BSTR bstr, //@parm String to find
  593. long Count, //@parm Max count of chars to search
  594. long Flags, //@parm Flags governing compares
  595. long * pDelta) //@parm Out parm to receive count of chars moved
  596. {
  597. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindText");
  598. return Finder(bstr, Count, Flags, pDelta, MOVE_IP);
  599. }
  600. /*
  601. * CTxtRange::FindTextEnd (bstr, cch, Flags, pLength)
  602. *
  603. * @mfunc
  604. * Starting from this range's End, search up to <p cch> characters
  605. * looking for the string <p bstr> subject to the compare flags
  606. * <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
  607. * <lt> 0 the search is backward. If the string is found, the range
  608. * limits are changed to be those of the matched string and *<p pLength>
  609. * is set equal to the length of the string. If the string isn't found,
  610. * the range remains unchanged and *<p pLength> is set equal to 0.
  611. *
  612. * @rdesc
  613. * HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
  614. */
  615. STDMETHODIMP CTxtRange::FindTextEnd (
  616. BSTR bstr, //@parm String to find
  617. long Count, //@parm Max count of chars to search
  618. long Flags, //@parm Flags governing compares
  619. long * pDelta) //@parm Out parm to receive count of chars moved
  620. {
  621. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindTextEnd");
  622. return Finder(bstr, Count, Flags, pDelta, MOVE_END);
  623. }
  624. /*
  625. * CTxtRange::FindTextStart (bstr, cch, Flags, pDelta)
  626. *
  627. * @mfunc
  628. * Starting from this range's Start, search up to <p cch> characters
  629. * looking for the string <p bstr> subject to the compare flags
  630. * <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
  631. * <lt> 0 the search is backward. If the string is found, the range
  632. * limits are changed to be those of the matched string and *<p pLength>
  633. * is set equal to the length of the string. If the string isn't found,
  634. * the range remains unchanged and *<p pLength> is set equal to 0.
  635. *
  636. * @rdesc
  637. * HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
  638. */
  639. STDMETHODIMP CTxtRange::FindTextStart (
  640. BSTR bstr, //@parm String to find
  641. long Count, //@parm Max count of chars to search
  642. long Flags, //@parm Flags governing compares
  643. long * pDelta) //@parm Out parm to receive count of chars moved
  644. {
  645. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindTextStart");
  646. return Finder(bstr, Count, Flags, pDelta, MOVE_START);
  647. }
  648. /*
  649. * CTxtRange::GetChar (pChar)
  650. *
  651. * @mfunc
  652. * Set *<p pChar> equal to the character at cpFirst
  653. *
  654. * @rdesc
  655. * HRESULT = (<p pChar>) NOERROR ? E_INVALIDARG
  656. *
  657. * @devnote
  658. * This method is very handy for walking a range character by character
  659. * from the Start. Accordingly, it's desirable that the active end
  660. * is at the Start. We set this up for a range, since the API doesn't
  661. * care which range end is active. But we can't do this for a selection,
  662. * since the selection API depends on the active end.
  663. */
  664. STDMETHODIMP CTxtRange::GetChar (
  665. long * pChar) //@parm Out parm for char
  666. {
  667. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetChar");
  668. HRESULT hr = GetLong(0, pChar);
  669. if(hr != NOERROR)
  670. return hr;
  671. if(_cch > 0) // Active end at cpMost (End)
  672. {
  673. if(_fSel)
  674. {
  675. CTxtPtr tp(_rpTX); // For selection, can't change
  676. tp.AdvanceCp(-_cch); // active end
  677. *pChar = (long)(tp.GetChar());
  678. return NOERROR;
  679. }
  680. FlipRange(); // For range, it's more efficient
  681. } // to work from cpFirst and API
  682. *(DWORD *)pChar = _rpTX.GetChar(); // doesn't expose RichEdit active
  683. // end
  684. return NOERROR;
  685. }
  686. /*
  687. * CTxtRange::GetDuplicate (ppRange)
  688. *
  689. * @mfunc
  690. * Get a clone of this range object. For example, you may want to
  691. * create an insertion point to traverse a range, so you clone the
  692. * range and then set the clone's cpLim equal to its cpFirst. A range
  693. * is characterized by its cpFirst, cpLim, and the story it belongs to.
  694. *
  695. * @rdesc
  696. * HRESULT = (if success) ? NOERROR :
  697. * (<p ppRange>) ? E_OUTOFMEMORY : E_INVALIDARG
  698. *
  699. * @comm
  700. * Even if this range is a selection, the clone returned is still only
  701. * a range.
  702. */
  703. STDMETHODIMP CTxtRange::GetDuplicate (
  704. ITextRange ** ppRange) //@parm Out parm to receive duplicate of range
  705. {
  706. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetDuplicate");
  707. HRESULT hr = GetLong(NULL, (LONG *)ppRange);
  708. if(hr != NOERROR)
  709. return hr;
  710. ITextRange *prg = new CTxtRange(*this);
  711. if(prg)
  712. {
  713. *ppRange = prg;
  714. prg->AddRef();
  715. return NOERROR;
  716. }
  717. return E_OUTOFMEMORY;
  718. }
  719. /*
  720. * ITextRange::GetEmbeddedObject (ppV)
  721. *
  722. * @mfunc
  723. * Property get method that gets a ptr to the object at cpFirst
  724. *
  725. * @rdesc
  726. * HRESULT = (!ppV) ? E_INVALIDARG :
  727. * (object found) ? NOERROR : S_FALSE
  728. */
  729. STDMETHODIMP CTxtRange::GetEmbeddedObject (
  730. IUnknown **ppV) //@parm Out parm to receive embedded object
  731. {
  732. HRESULT hr = GetLong(NULL, (LONG *)ppV);
  733. if(hr != NOERROR)
  734. return hr;
  735. if(GetObjectCount())
  736. {
  737. COleObject *pobj = GetPed()->_pobjmgr->GetObjectFromCp(GetCpMin());
  738. if(pobj && (*ppV = pobj->GetIUnknown()) != NULL)
  739. {
  740. (*ppV)->AddRef();
  741. return NOERROR;
  742. }
  743. }
  744. return S_FALSE;
  745. }
  746. /*
  747. * CTxtRange::GetEnd (pcpLim)
  748. *
  749. * @mfunc
  750. * Get this range's End (cpMost) cp
  751. *
  752. * @rdesc
  753. * HRESULT = (<p pcpLim>) ? NOERROR : E_INVALIDARG
  754. */
  755. STDMETHODIMP CTxtRange::GetEnd (
  756. long * pcpLim) //@parm Out parm to receive End cp value
  757. {
  758. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetEnd");
  759. return GetLong(GetCpMost(), pcpLim);
  760. }
  761. /*
  762. * CTxtRange::GetFont (ppFont)
  763. *
  764. * @mfunc
  765. * Get an ITextFont object with the character attributes of this range
  766. *
  767. * @rdesc
  768. * HRESULT = <p ppFont> ? NOERROR : E_INVALIDARG
  769. */
  770. STDMETHODIMP CTxtRange::GetFont (
  771. ITextFont ** ppFont) //@parm Out parm to receive ITextFont object
  772. {
  773. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFont");
  774. HRESULT hr = GetLong(NULL, (LONG *)ppFont);
  775. if(hr != NOERROR)
  776. return hr;
  777. *ppFont = (ITextFont *) new CTxtFont(this);
  778. return *ppFont ? NOERROR : E_OUTOFMEMORY;
  779. }
  780. /*
  781. * CTxtRange::GetFormattedText (ppRange)
  782. *
  783. * @mfunc
  784. * Retrieves an ITextRange with this range's formatted text.
  785. * If <p ppRange> is NULL, the clipboard is the target.
  786. *
  787. * @rdesc
  788. * HRESULT = (if success) ? NOERROR :
  789. * (<p ppRange>) ? E_OUTOFMEMORY : E_INVALIDARG
  790. */
  791. STDMETHODIMP CTxtRange::GetFormattedText (
  792. ITextRange ** ppRange) //@parm Out parm to receive formatted text
  793. {
  794. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFormattedText");
  795. return GetDuplicate(ppRange);
  796. }
  797. /*
  798. * CTxtRange::GetIndex (Unit, pIndex)
  799. *
  800. * @mfunc
  801. * Set *<p pIndex> equal to the Unit number at this range's cpFirst
  802. *
  803. * @rdesc
  804. * HRESULT = (!<p pIndex>) ? E_INVALIDARG :
  805. * (Unit not implemented) ? E_NOTIMPL :
  806. * (Unit available) ? NOERROR : S_FALSE
  807. * @future
  808. * implement tomWindow?
  809. */
  810. STDMETHODIMP CTxtRange::GetIndex (
  811. long Unit, //@parm Unit to index
  812. long * pIndex) //@parm Out parm to receive index value
  813. {
  814. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetIndex");
  815. HRESULT hr = GetLong(0, pIndex);
  816. if(hr != NOERROR)
  817. return hr;
  818. LONG cp;
  819. LONG cUnit = tomBackward;
  820. CTxtRange rg(*this);
  821. hr = rg.Expander(Unit, FALSE, NULL, // Go to Start of Unit; else
  822. &cp, NULL); // UnitCounter gives 1 extra
  823. if(FAILED(hr))
  824. return hr; // Unit not recognized
  825. LONG cch = rg.UnitCounter(Unit, cUnit, 0);
  826. if(cch == tomForward) // UnitCounter() doesn't know
  827. return E_NOTIMPL; // Unit
  828. if(cch == tomBackward) // Unit not in story
  829. return S_FALSE;
  830. *pIndex = -cUnit + 1; // Make count positive and
  831. // 1-based
  832. return NOERROR;
  833. }
  834. /*
  835. * CTxtRange::GetPara (ppPara)
  836. *
  837. * @mfunc
  838. * Get an ITextPara object with the paragraph attributes of this range
  839. *
  840. * @rdesc
  841. * HRESULT = <p ppPara> ? NOERROR : E_INVALIDARG
  842. */
  843. STDMETHODIMP CTxtRange::GetPara (
  844. ITextPara ** ppPara) //@parm Out parm to receive ITextPara object
  845. {
  846. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetPara");
  847. HRESULT hr = GetLong(NULL, (LONG *)ppPara);
  848. if(hr != NOERROR)
  849. return hr;
  850. *ppPara = (ITextPara *) new CTxtPara(this);
  851. return *ppPara ? NOERROR : E_OUTOFMEMORY;
  852. }
  853. /*
  854. * CTxtRange::GetPoint (px, py, Type)
  855. *
  856. * @mfunc
  857. * Get point for selection Start or End and intraline position
  858. * as determined by <p Type>.
  859. *
  860. * @rdesc
  861. * HRESULT = (!<p px> or !<p py>) ? E_INVALIDARG :
  862. * (if success) ? NOERROR : S_FALSE
  863. */
  864. STDMETHODIMP CTxtRange::GetPoint (
  865. long Type, //@parm Type of point
  866. long * px, //@parm Out parm for x coordinate
  867. long * py) //@parm Out parm for y coordinate
  868. {
  869. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetPoint");
  870. if(!px || !py)
  871. return E_INVALIDARG;
  872. *px = *py = 0;
  873. if(IsZombie())
  874. return CO_E_RELEASED;
  875. LONG ili;
  876. BOOL fAtEnd = _cch > 0; // Default value for IP
  877. POINT pt;
  878. CRchTxtPtr rtp(*this); // Default active end
  879. CTxtEdit * ped = GetPed();
  880. CDisplay * pdp = ped->_pdp; // Save indirections
  881. if(!pdp || !ped->fInplaceActive())
  882. return E_FAIL; // No display or not active
  883. // then we can do nothing.
  884. if(fAtEnd ^ !(Type & tomStart)) // Move tp to active end
  885. rtp.Advance(-_cch);
  886. ili = pdp->PointFromTp(rtp, NULL, fAtEnd, pt, NULL, Type & 0x1f);
  887. RECT rcView; // Verify return value makes
  888. // sense since PointFromTp
  889. pdp->GetViewRect(rcView, NULL); // may return values outside
  890. // client rect
  891. rcView.bottom++; // Enlarge Rect to include
  892. rcView.right++; // bottom and right edges
  893. if(ili >= 0 && PtInRect(&rcView, pt)) // Function succeeded
  894. {
  895. // Caller wants screen coordinates?
  896. if ( !(Type & tomClientCoord) )
  897. ped->TxClientToScreen(&pt);
  898. *px = pt.x;
  899. *py = pt.y;
  900. return NOERROR;
  901. }
  902. return S_FALSE; // Function failed
  903. }
  904. /*
  905. * CTxtRange::GetStart (pcpFirst)
  906. *
  907. * @mfunc
  908. * Get this range's Start (cpMin) cp
  909. *
  910. * @rdesc
  911. * HRESULT = (<p pcpFirst>) ? NOERROR : E_INVALIDARG
  912. */
  913. STDMETHODIMP CTxtRange::GetStart (
  914. long * pcpFirst) //@parm Out parm to receive Start cp value
  915. {
  916. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStart");
  917. return GetLong(GetCpMin(), pcpFirst);
  918. }
  919. /*
  920. * CTxtRange::GetStoryLength (pcch)
  921. *
  922. * @mfunc
  923. * Set *<p pcch> = count of chars in this range's story
  924. *
  925. * @rdesc
  926. * HRESULT = (<p pcch>) ? NOERROR : E_INVALIDARG
  927. */
  928. STDMETHODIMP CTxtRange::GetStoryLength (
  929. long * pcch) //@parm Out parm to get length of this range's story
  930. {
  931. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStoryLength");
  932. return GetLong(GetTextLength(), pcch);
  933. }
  934. /*
  935. * ITextRange::GetStoryType (pValue)
  936. *
  937. * @mfunc
  938. * Property get method that gets the type of this range's
  939. * story.
  940. *
  941. * @rdesc
  942. * HRESULT = (pValue) NOERROR ? E_INVALIDARG
  943. */
  944. STDMETHODIMP CTxtRange::GetStoryType (
  945. long *pValue) //@parm Out parm to get type of this range's story
  946. {
  947. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStoryType");
  948. return GetLong(tomUnknownStory, pValue);
  949. }
  950. /*
  951. * CTxtRange::GetText (pbstr)
  952. *
  953. * @mfunc
  954. * Get plain text in this range. The Text property is the default
  955. * property for ITextRange.
  956. *
  957. * @rdesc
  958. * HRESULT = (if success) ? NOERROR :
  959. * (!<p pbstr>) ? E_INVALIDARG : E_OUTOFMEMORY
  960. */
  961. STDMETHODIMP CTxtRange::GetText (
  962. BSTR * pbstr) //@parm Out parm to receive bstr
  963. {
  964. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetText");
  965. HRESULT hr = GetLong(NULL, (LONG *)pbstr);
  966. if(hr != NOERROR)
  967. return hr;
  968. if(!GetCch())
  969. return NOERROR;
  970. LONG cpMin, cpMost;
  971. LONG cch = GetRange(cpMin, cpMost);
  972. *pbstr = SysAllocStringLen(NULL, cch);
  973. if(!*pbstr)
  974. return E_OUTOFMEMORY;
  975. CTxtPtr tp(_rpTX);
  976. tp.SetCp(cpMin);
  977. tp.GetText( cch, (TCHAR*) * pbstr );
  978. return NOERROR;
  979. }
  980. /*
  981. * CTxtRange::InRange (pRange, pB)
  982. *
  983. * @mfunc
  984. * Returns *<p pB> = tomTrue iff this range points within or at the same
  985. * text as <p pRange> does
  986. *
  987. * @rdesc
  988. * HRESULT = (within range) ? NOERROR : S_FALSE
  989. */
  990. STDMETHODIMP CTxtRange::InRange (
  991. ITextRange * pRange, //@parm ITextRange to compare with
  992. long * pB) //@parm Out parm for comparison result
  993. {
  994. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::InRange");
  995. return IsTrue(Comparer(pRange), pB);
  996. }
  997. /*
  998. * CTxtRange::InStory (pRange, pB)
  999. *
  1000. * @mfunc
  1001. * Return *pB = tomTrue iff this range's story is the same as
  1002. * <p pRange>'s
  1003. *
  1004. * @rdesc
  1005. * HRESULT = (in story) ? NOERROR : S_FALSE
  1006. *
  1007. * @future
  1008. * If RichEdit acquires the ability to have multiple stories and
  1009. * therefore ranges get a _story member, then compare that member
  1010. * instead of calling _rpTX.SameRuns().
  1011. */
  1012. STDMETHODIMP CTxtRange::InStory (
  1013. ITextRange *pRange, //@parm ITextRange to query for private interface
  1014. long * pB) //@parm Out parm to receive tomBool result
  1015. {
  1016. #ifndef PEGASUS
  1017. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::InStory");
  1018. return IsTrue(IsSameVtables(this, pRange) && // Same vtables,
  1019. _rpTX.SameRuns(&((CTxtRange *)pRange)->_rpTX), // same Runs
  1020. pB);
  1021. #else
  1022. return 0;
  1023. #endif
  1024. }
  1025. /*
  1026. * CTxtRange::IsEqual (pRange, pB)
  1027. *
  1028. * @mfunc
  1029. * Returns *<p pB> = tomTrue iff this range points at the same text (cp's
  1030. * and story) as <p pRange> does and pB isn't NULL.
  1031. *
  1032. * @rdesc
  1033. * HRESULT = (equal objects) ? NOERROR : S_FALSE
  1034. */
  1035. STDMETHODIMP CTxtRange::IsEqual (
  1036. ITextRange * pRange, //@parm ITextRange to compare with
  1037. long * pB) //@parm Out parm for comparison result
  1038. {
  1039. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::IsEqual");
  1040. return IsTrue(Comparer(pRange) < 0, pB);
  1041. }
  1042. /*
  1043. * CTxtRange::Move (Unit, Count, pDelta)
  1044. *
  1045. * @mfunc
  1046. * Move end(s) <p Count> <p Unit>'s, returning *<p pDelta> = # units
  1047. * actually moved. In general, this method converts a range into an
  1048. * insertion point if it isn't already, and moves the insertion point.
  1049. * If <p Count> <gt> 0, motion is forward toward the end of the story;
  1050. * if <p Count> <lt> 0, motion is backward toward the beginning.
  1051. * <p Count> = 0 moves cpFirst to the beginning of the <p Unit>
  1052. * containing cpFirst.
  1053. *
  1054. * @rdesc
  1055. * HRESULT = (if change) ? NOERROR :
  1056. * (if Unit supported) ? S_FALSE : E_NOTIMPL
  1057. * @devnote
  1058. * Argument validation of the three Move methods is done by the helper
  1059. * function CTxtRange::Mover(Unit, Count, pDelta, Mode)
  1060. */
  1061. STDMETHODIMP CTxtRange::Move (
  1062. long Unit, //@parm Unit to use
  1063. long Count, //@parm Number of Units to move
  1064. long * pDelta) //@parm Out parm to receive actual count of
  1065. // Units end is moved
  1066. {
  1067. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Move");
  1068. CCallMgr callmgr(GetPed());
  1069. return Mover(Unit, Count, pDelta, MOVE_IP);
  1070. }
  1071. /*
  1072. * CTxtRange::MoveEnd (Unit, Count, pDelta)
  1073. *
  1074. * @mfunc
  1075. * Move End end <p Count> <p Unit>'s, returning *<p pDelta> = # units
  1076. * actually moved
  1077. *
  1078. * @rdesc
  1079. * HRESULT = (if change) ? NOERROR :
  1080. * (if Unit supported) ? S_FALSE : E_NOTIMPL
  1081. */
  1082. STDMETHODIMP CTxtRange::MoveEnd (
  1083. long Unit, //@parm Unit to use
  1084. long Count, //@parm Number of Units to move
  1085. long * pDelta) //@parm Out parm to receive actual count of
  1086. // Units end is moved
  1087. {
  1088. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEnd");
  1089. CCallMgr callmgr(GetPed());
  1090. return Mover(Unit, Count, pDelta, MOVE_END);
  1091. }
  1092. /*
  1093. * CTxtRange::MoveEndUntil (Cset, Count, pDelta)
  1094. *
  1095. * @mfunc
  1096. * Move the End just past all contiguous characters that are not found
  1097. * in the set of characters specified by the VARIANT <p Cset> parameter.
  1098. *
  1099. * @rdesc
  1100. * HRESULT = (if change) ? NOERROR :
  1101. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1102. */
  1103. STDMETHODIMP CTxtRange::MoveEndUntil (
  1104. VARIANT * Cset, //@parm Character match set to use
  1105. long Count, //@parm Max number of characters to move past
  1106. long * pDelta) //@parm Out parm to receive actual count of
  1107. // characters end is moved
  1108. {
  1109. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEndUntil");
  1110. return Matcher(Cset, Count, pDelta, MOVE_END, MATCH_UNTIL);
  1111. }
  1112. /*
  1113. * CTxtRange::MoveEndWhile (Cset, Count, pDelta)
  1114. *
  1115. * @mfunc
  1116. * Move the End just past all contiguous characters that are found in
  1117. * the set of characters specified by the VARIANT <p Cset> parameter.
  1118. *
  1119. * @rdesc
  1120. * HRESULT = (if change) ? NOERROR :
  1121. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1122. */
  1123. STDMETHODIMP CTxtRange::MoveEndWhile (
  1124. VARIANT * Cset, //@parm Character match set to use
  1125. long Count, //@parm Max number of characters to move past
  1126. long * pDelta) //@parm Out parm to receive actual count of
  1127. // characters end is moved
  1128. {
  1129. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEndWhile");
  1130. return Matcher(Cset, Count, pDelta, MOVE_END, MATCH_WHILE);
  1131. }
  1132. /*
  1133. * CTxtRange::MoveStart (Unit, Count, pDelta)
  1134. *
  1135. * @mfunc
  1136. * Move Start end <p Count> <p Unit>'s, returning *<p pDelta> = # units
  1137. * actually moved
  1138. *
  1139. * @rdesc
  1140. * HRESULT = (if change) ? NOERROR :
  1141. * (if Unit supported) ? S_FALSE : E_NOTIMPL
  1142. */
  1143. STDMETHODIMP CTxtRange::MoveStart (
  1144. long Unit, //@parm Unit to use
  1145. long Count, //@parm Number of Units to move
  1146. long * pDelta) //@parm Out parm to receive actual count of
  1147. // Units end is moved
  1148. {
  1149. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStart");
  1150. CCallMgr callmgr(GetPed());
  1151. return Mover(Unit, Count, pDelta, MOVE_START);
  1152. }
  1153. /*
  1154. * CTxtRange::MoveStartUntil (Cset, Count, pDelta)
  1155. *
  1156. * @mfunc
  1157. * Move the Start just past all contiguous characters that are not found
  1158. * in the set of characters specified by the VARIANT <p Cset> parameter.
  1159. *
  1160. * @rdesc
  1161. * HRESULT = (if change) ? NOERROR :
  1162. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1163. */
  1164. STDMETHODIMP CTxtRange::MoveStartUntil (
  1165. VARIANT * Cset, //@parm Character match set to use
  1166. long Count, //@parm Max number of characters to move past
  1167. long * pDelta) //@parm Out parm to receive actual count of
  1168. // characters end is moved
  1169. {
  1170. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStartUntil");
  1171. return Matcher(Cset, Count, pDelta, MOVE_START, MATCH_UNTIL);
  1172. }
  1173. /*
  1174. * CTxtRange::MoveStartWhile (Cset, Count, pDelta)
  1175. *
  1176. * @mfunc
  1177. * Move the Start just past all contiguous characters that are found in
  1178. * the set of characters specified by the VARIANT <p Cset> parameter.
  1179. *
  1180. * @rdesc
  1181. * HRESULT = (if change) ? NOERROR :
  1182. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1183. */
  1184. STDMETHODIMP CTxtRange::MoveStartWhile (
  1185. VARIANT * Cset, //@parm Character match set to use
  1186. long Count, //@parm Max number of characters to move past
  1187. long * pDelta) //@parm Out parm to receive actual count of
  1188. // characters end is moved
  1189. {
  1190. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStartWhile");
  1191. return Matcher(Cset, Count, pDelta, MOVE_START, MATCH_WHILE);
  1192. }
  1193. /*
  1194. * CTxtRange::MoveUntil (Cset,Count, pDelta)
  1195. *
  1196. * @mfunc
  1197. * Convert this range into an insertion point if it isn't already,
  1198. * and keep moving the insertion point until encountering any
  1199. * character in the set of characters specified by the VARIANT cset
  1200. * parameter.
  1201. *
  1202. * @rdesc
  1203. * HRESULT = (if change) ? NOERROR :
  1204. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1205. */
  1206. STDMETHODIMP CTxtRange::MoveUntil (
  1207. VARIANT * Cset, //@parm Character match set to use
  1208. long Count, //@parm Max number of characters to move past
  1209. long * pDelta) //@parm Out parm to receive actual count of
  1210. // characters end is moved
  1211. {
  1212. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveUntil");
  1213. return Matcher(Cset, Count, pDelta, MOVE_IP, MATCH_UNTIL);
  1214. }
  1215. /*
  1216. * CTxtRange::MoveWhile (Cset, Count, pDelta)
  1217. *
  1218. * @mfunc
  1219. * Convert this range into an insertion point if it isn't already,
  1220. * and keep moving the insertion point so long as (while) the
  1221. * characters past by are found in set of characters specified by
  1222. * the VARIANT cset parameter. Such a contiguous set of characters
  1223. * is known as a span of characters. The magnitude of the <p Count>
  1224. * parameter gives the maximum number of characters to move past and
  1225. * the sign of <p Count> specifies the direction to move in.
  1226. *
  1227. * @rdesc
  1228. * HRESULT = (if change) ? NOERROR :
  1229. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  1230. *
  1231. * @devnote
  1232. * Argument validation of the MoveWhile and MoveUntil methods is done by
  1233. * the helper CTxtRange::Matcher (Cset, Count, pDelta, fExtend, fSpan)
  1234. */
  1235. STDMETHODIMP CTxtRange::MoveWhile (
  1236. VARIANT * Cset, //@parm Character match set to use
  1237. long Count, //@parm Max number of characters to move past
  1238. long * pDelta) //@parm Out parm to receive actual count of
  1239. // characters end is moved
  1240. {
  1241. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveWhile");
  1242. return Matcher(Cset, Count, pDelta, MOVE_IP, MATCH_WHILE);
  1243. }
  1244. /*
  1245. * CTxtRange::Paste (pVar, ClipboardFormat)
  1246. *
  1247. * @mfunc
  1248. * Paste the data object <p pVar> into this range. If
  1249. * <p pVar> is null, paste from the clipboard.
  1250. *
  1251. * @rdesc
  1252. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  1253. * (if success) ? NOERROR : E_OUTOFMEMORY
  1254. */
  1255. STDMETHODIMP CTxtRange::Paste (
  1256. VARIANT *pVar, //@parm Data object to paste
  1257. long ClipboardFormat) //@parm Desired clipboard format
  1258. {
  1259. #ifndef PEGASUS
  1260. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Paste");
  1261. if(IsZombie())
  1262. return CO_E_RELEASED;
  1263. CCallMgr callmgr(GetPed());
  1264. HRESULT hr;
  1265. IDataObject * pdo = NULL; // Default clipboard
  1266. IUndoBuilder * publdr;
  1267. CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
  1268. if(WriteAccessDenied())
  1269. return E_ACCESSDENIED;
  1270. if(pVar)
  1271. if (pVar->vt == VT_UNKNOWN)
  1272. pVar->punkVal->QueryInterface(IID_IDataObject, (void **)&pdo);
  1273. else if (pVar->vt == (VT_UNKNOWN | VT_BYREF))
  1274. pdo = (IDataObject *)(*pVar->ppunkVal);
  1275. hr = GetPed()->PasteDataObjectToRange (pdo, this,
  1276. (WORD)ClipboardFormat, NULL, publdr, PDOR_NONE);
  1277. if(pdo && pVar->vt == VT_UNKNOWN)
  1278. pdo->Release();
  1279. Update(TRUE); // Update selection
  1280. return hr;
  1281. #else
  1282. return 0;
  1283. #endif
  1284. }
  1285. /*
  1286. * ITextRange::ScrollIntoView(long Code)
  1287. *
  1288. * @mfunc
  1289. * Method that scrolls this range into view according to the
  1290. * code Code defined below.
  1291. *
  1292. * @rdesc
  1293. * HRESULT = (if success) ? NOERROR : S_FALSE
  1294. */
  1295. STDMETHODIMP CTxtRange::ScrollIntoView (
  1296. long Code) //@parm Scroll code
  1297. {
  1298. #ifndef PEGASUS
  1299. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::ScrollIntoView");
  1300. // Check for invalid bits
  1301. if (Code & ~(tomStart + tomEnd + TA_LEFT + TA_TOP + TA_BOTTOM +
  1302. TA_CENTER + TA_STARTOFLINE + TA_ENDOFLINE + TA_LOGICAL))
  1303. return E_INVALIDARG;
  1304. // Validate parameter
  1305. long lCode = tomEnd;
  1306. if (Code & tomStart)
  1307. lCode = tomStart;
  1308. Code &= ~tomStart;
  1309. if(IsZombie())
  1310. return CO_E_RELEASED;
  1311. // Get local copy of ped to save some indirections.
  1312. CTxtEdit *ped = GetPed();
  1313. if (!ped->fInplaceActive())
  1314. {
  1315. // If the control is not active, we can't get the information
  1316. // because no one knows what our client rect is.
  1317. return E_FAIL;
  1318. }
  1319. // Get a local copy of display to save some indirections.
  1320. CDisplay *pdp = ped->_pdp;
  1321. if (pdp->IsFrozen())
  1322. {
  1323. return E_FAIL;
  1324. }
  1325. LONG cpStart;
  1326. LONG cpForEnd;
  1327. GetRange(cpStart, cpForEnd);
  1328. // Get the view rectangle so we can compute the absolute x/y
  1329. RECT rcView;
  1330. pdp->GetViewRect(rcView, NULL);
  1331. // Set up tp for PointFromTp call
  1332. CRchTxtPtr rtp(*this);
  1333. if(_cch > 0)
  1334. rtp.Advance(-_cch);
  1335. // Values used for making returned point locations absolute since
  1336. // PointFromTp adjusts the point returned to be relative to the
  1337. // display.
  1338. const LONG xScrollAdj = pdp->GetXScroll() - rcView.left;
  1339. const LONG yScrollAdj = pdp->GetYScroll() - rcView.top;
  1340. // Get the left/top for the start
  1341. BOOL taMask = Code & TA_STARTOFLINE; //set beginning of line flag
  1342. BOOL fAtEnd = _cch ? TRUE : !rtp._rpTX.IsAfterEOP();
  1343. POINT ptStart;
  1344. CLinePtr rpStart(pdp);
  1345. LONG iliStart = pdp->PointFromTp(rtp, NULL, _cch ? FALSE : fAtEnd,
  1346. ptStart, &rpStart, (lCode == tomStart && Code) ? Code :
  1347. TA_TOP + TA_LEFT);
  1348. ptStart.x += xScrollAdj;
  1349. ptStart.y += yScrollAdj;
  1350. // Get the right/bottom for the end
  1351. rtp.SetCp(cpForEnd);
  1352. POINT ptEnd;
  1353. CLinePtr rpEnd(pdp);
  1354. LONG iliEnd = pdp->PointFromTp(rtp, NULL, fAtEnd, ptEnd, &rpEnd,
  1355. (lCode == tomEnd && Code) ? Code :
  1356. TA_BOTTOM + TA_RIGHT);
  1357. ptEnd.x += xScrollAdj;
  1358. ptEnd.y += yScrollAdj;
  1359. //
  1360. // Calculate the yScroll
  1361. //
  1362. // The basic idea is to display both the start and the end if possible. But
  1363. // if it is not possible then display the requested end based on the input
  1364. // parameter.
  1365. LONG yHeightView = pdp->GetViewHeight();
  1366. LONG yScroll;
  1367. if (tomStart == lCode)
  1368. {
  1369. // Scroll the Start cp to the top of the view
  1370. yScroll = ptStart.y;
  1371. }
  1372. else
  1373. {
  1374. // Scroll the End cp to the bottom of the view
  1375. yScroll = ptEnd.y - yHeightView;
  1376. }
  1377. //
  1378. // Calculate the X Scroll
  1379. //
  1380. // Default scroll to beginning of the line
  1381. LONG xScroll = 0;
  1382. // Make view local to save a number of indirections
  1383. LONG xWidthView = pdp->GetViewWidth();
  1384. if (iliStart == iliEnd)
  1385. {
  1386. // Entire selection is on the same line so we want to display as
  1387. // much of it as is possible.
  1388. LONG xWidthSel = ptEnd.x - ptStart.x;
  1389. if (xWidthSel > xWidthView)
  1390. {
  1391. // Selection length is greater than display width
  1392. if (tomStart == lCode)
  1393. {
  1394. // Show Start requested - just start from beginning
  1395. // of selection
  1396. xScroll = ptStart.x;
  1397. }
  1398. else
  1399. {
  1400. // Show end requested - show as much of selection as
  1401. // possible, ending with last character in the
  1402. // selection.
  1403. xScroll = ptEnd.x - xWidthView;
  1404. }
  1405. }
  1406. else if (xWidthSel < 0)
  1407. {
  1408. xWidthSel = -xWidthSel;
  1409. if (xWidthSel > xWidthView)
  1410. {
  1411. if (tomStart == lCode)
  1412. {
  1413. // Show Requested Start;
  1414. xScroll = max(0, ptStart.x - xWidthView);
  1415. }
  1416. else
  1417. {
  1418. xScroll = max(0, ptEnd.x - xWidthView);
  1419. }
  1420. }
  1421. else if (ptEnd.x > xWidthView || ptStart.x > xWidthView)
  1422. {
  1423. // Check mask if position is outside the boundaries
  1424. if (taMask)
  1425. xScroll = ptStart.x - xWidthView;
  1426. else
  1427. xScroll = ptEnd.x - xWidthView;
  1428. }
  1429. }
  1430. else if (ptEnd.x > xWidthView || ptStart.x > xWidthView)
  1431. {
  1432. // Check mask if position is outside the boundaries
  1433. if (taMask)
  1434. xScroll = ptStart.x - xWidthView;
  1435. else
  1436. xScroll = ptEnd.x - xWidthView;
  1437. }
  1438. }
  1439. else
  1440. {
  1441. // Multiline selection. Display as much as possible of the requested
  1442. // end's line.
  1443. // Calc width of line
  1444. LONG xWidthLine = (tomStart == lCode)
  1445. ? rpStart->_xWidth + rpStart->_xLeft
  1446. : rpEnd->_xWidth + rpEnd->_xLeft;
  1447. // If line width is less than or equal to view, start at
  1448. // 0 otherwise we need to adjust starting position to
  1449. // show as much of the requested end's selection line
  1450. // as possible.
  1451. if(xWidthLine > xWidthView)
  1452. {
  1453. if(tomStart == lCode)
  1454. {
  1455. // Start end to be displayed
  1456. if(xWidthLine - ptStart.x > xWidthView)
  1457. {
  1458. // Selection is bigger than view, so start at beginning
  1459. // and display as much as possible.
  1460. xScroll = ptStart.x;
  1461. }
  1462. else
  1463. {
  1464. // Remember that this is a multiline selection so the
  1465. // selection on this line goes from ptStart.x to the
  1466. // end of line. Since the selection width is less than
  1467. // the width of the view, we just back up the width
  1468. // of view to show the entire selection.
  1469. xScroll = xWidthLine - xWidthView;
  1470. }
  1471. }
  1472. else
  1473. {
  1474. // Show the end of the selection. In the multiline case,
  1475. // this goes from the beginning of the line to End. So
  1476. // we only have to adjust if the End is beyond the view.
  1477. if(ptEnd.x > xWidthView)
  1478. {
  1479. // End beyond the view. Show as much as possible
  1480. // of the selection.
  1481. xScroll = ptEnd.x - xWidthView;
  1482. }
  1483. }
  1484. }
  1485. }
  1486. // Do the scroll
  1487. pdp->ScrollView(xScroll, yScroll, FALSE, FALSE);
  1488. return S_OK;
  1489. #else
  1490. return 0;
  1491. #endif
  1492. }
  1493. /*
  1494. * CTxtRange::Select ()
  1495. *
  1496. * @mfunc
  1497. * Copy this range's cp's and story ptr to the active selection.
  1498. *
  1499. * @rdesc
  1500. * HRESULT = (if selection exists) ? NOERROR : S_FALSE
  1501. */
  1502. STDMETHODIMP CTxtRange::Select ()
  1503. {
  1504. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Select");
  1505. if(IsZombie())
  1506. return CO_E_RELEASED;
  1507. CTxtSelection *pSel = GetPed()->GetSel();
  1508. if(pSel)
  1509. {
  1510. LONG cpMin, cpMost;
  1511. GetRange(cpMin, cpMost);
  1512. pSel->SetRange(cpMin, cpMost);
  1513. return NOERROR;
  1514. }
  1515. return S_FALSE;
  1516. }
  1517. /*
  1518. * CTxtRange::SetChar (Char)
  1519. *
  1520. * @mfunc
  1521. * Set char at cpFirst = <p Char>
  1522. *
  1523. * @rdesc
  1524. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  1525. * (char stored) ? NOERROR : S_FALSE
  1526. *
  1527. * @devnote
  1528. * Special cases could be much faster, e.g., just overtype the plain-
  1529. * text backing store unless at EOD or EOR. Code below uses a cloned
  1530. * range to handle all cases easily and preserve undo capability.
  1531. */
  1532. STDMETHODIMP CTxtRange::SetChar (
  1533. long Char) //@parm New value for char at cpFirst
  1534. {
  1535. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetChar");
  1536. if(IsZombie())
  1537. return CO_E_RELEASED;
  1538. CTxtEdit * ped = GetPed();
  1539. CCallMgr callmgr(ped);
  1540. TCHAR ch = (TCHAR)Char; // Avoid endian problems
  1541. CTxtRange rg(*this);
  1542. IUndoBuilder * publdr;
  1543. CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
  1544. CFreezeDisplay fd(ped->_pdp);
  1545. if(WriteAccessDenied())
  1546. return E_ACCESSDENIED;
  1547. if(!ped->_pdp->IsMultiLine() && IsEOP(Char))// EOPs are not allowed in
  1548. return FALSE; // single-line edit controls
  1549. undobldr.StopGroupTyping();
  1550. rg.Collapser(tomStart); // Collapse at cpMin
  1551. rg.SetExtend(TRUE); // Setup to select
  1552. rg.Advance(1); // Try to select char at IP
  1553. ped->OrCharFlags(GetCharFlags(ch), publdr);
  1554. if(rg.ReplaceRange(1, &ch, publdr, SELRR_REMEMBERRANGE))
  1555. {
  1556. Update(TRUE); // Update selection
  1557. return NOERROR;
  1558. }
  1559. return S_FALSE;
  1560. }
  1561. /*
  1562. * CTxtRange::SetEnd (cp)
  1563. *
  1564. * @mfunc
  1565. * Set this range's End cp
  1566. *
  1567. * @rdesc
  1568. * HRESULT = (if change) ? NOERROR : S_FALSE
  1569. *
  1570. * @comm
  1571. * Note that setting this range's cpMost to <p cp> also sets cpMin to
  1572. * <p cp> if <p cp> < cpMin.
  1573. */
  1574. STDMETHODIMP CTxtRange::SetEnd (
  1575. long cp) //@parm Desired new End cp
  1576. {
  1577. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetEnd");
  1578. if(IsZombie())
  1579. return CO_E_RELEASED;
  1580. LONG cpMin = GetCpMin();
  1581. ValidateCp(cp);
  1582. return SetRange(min(cpMin, cp), cp); // Active end is End
  1583. }
  1584. /*
  1585. * CTxtRange::SetFont (pFont)
  1586. *
  1587. * @mfunc
  1588. * Set this range's character attributes to those given by <p pFont>.
  1589. * This method is a "character format painter".
  1590. *
  1591. * @rdesc
  1592. * HRESULT = (!pFont) ? E_INVALIDARG :
  1593. * (if success) ? NOERROR :
  1594. * (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
  1595. */
  1596. STDMETHODIMP CTxtRange::SetFont (
  1597. ITextFont * pFont) //@parm Font object with desired character formatting
  1598. {
  1599. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFont");
  1600. if(!pFont)
  1601. return E_INVALIDARG;
  1602. if(IsZombie())
  1603. return CO_E_RELEASED;
  1604. ITextFont *pFontApply = (ITextFont *) new CTxtFont(this);
  1605. if(!pFontApply)
  1606. return E_OUTOFMEMORY;
  1607. HRESULT hr;
  1608. if(*(LONG *)pFontApply == *(LONG *)pFont) // If same vtable, use
  1609. hr = CharFormatSetter(&((CTxtFont *)pFont)->_CF, // its copy
  1610. ((CTxtFont *)pFont)->_dwMask);
  1611. else // Else copy
  1612. hr = pFontApply->SetDuplicate(pFont); // to clone and apply
  1613. pFontApply->Release();
  1614. return hr;
  1615. }
  1616. /*
  1617. * CTxtRange::SetFormattedText (pRange)
  1618. *
  1619. * @mfunc
  1620. * Replace this range's text with formatted text given by <p pRange>.
  1621. * If <p pRange> is NULL, paste from the clipboard.
  1622. *
  1623. * @rdesc
  1624. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  1625. * (if success) ? NOERROR : E_OUTOFMEMORY
  1626. *
  1627. * @FUTURE
  1628. * Do this more efficiently if pRange points at a RichEdit range. This
  1629. * would also help with RichEdit D&D to RichEdit targets
  1630. */
  1631. STDMETHODIMP CTxtRange::SetFormattedText (
  1632. ITextRange * pRange) //@parm Formatted text to replace this
  1633. // range's text
  1634. {
  1635. #ifndef PEGASUS
  1636. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFormattedText");
  1637. if(IsZombie())
  1638. return CO_E_RELEASED;
  1639. CCallMgr callmgr(GetPed());
  1640. LONG cpMin = GetCpMin();
  1641. HRESULT hr;
  1642. IUnknown * pdo = NULL;
  1643. VARIANT vr;
  1644. if(!pRange)
  1645. return NOERROR; // Nothing to paste
  1646. if(WriteAccessDenied())
  1647. return E_ACCESSDENIED;
  1648. VariantInit(&vr);
  1649. vr.vt = VT_UNKNOWN | VT_BYREF;
  1650. vr.ppunkVal = &pdo;
  1651. hr = pRange->Copy(&vr);
  1652. if(hr == NOERROR)
  1653. {
  1654. hr = Paste(&vr, 0);
  1655. pdo->Release(); // Release the data object
  1656. _cch = GetCp() - cpMin; // Select the new text
  1657. }
  1658. return hr;
  1659. #else
  1660. return 0;
  1661. #endif
  1662. }
  1663. /*
  1664. * CTxtRange::SetIndex (Unit, Index, Extend)
  1665. *
  1666. * @mfunc
  1667. * If <p Extend> is zero, convert this range into an insertion point
  1668. * at the start of the <p Index>th <p Unit> in the current story. If
  1669. * <p Extend> is nonzero, set this range to consist of this unit. The
  1670. * start of the story corresponds to <p Index> = 0 for all units.
  1671. *
  1672. * Positive indices are 1-based and index relative to the beginning of
  1673. * the story. Negative indices are -1-based and index relative to the
  1674. * end of the story. So an index of 1 refers to the first Unit in the
  1675. * story and an index of -1 refers to the last Unit in the story.
  1676. *
  1677. * @rdesc
  1678. * HRESULT = (invalid index) ? E_INVALIDARG :
  1679. * (Unit not supported) ? E_NOTIMPL :
  1680. * (change) ? NOERROR : S_FALSE
  1681. *
  1682. * @devnote
  1683. * Currently moves out <p Index> <p Unit>s from the start of the story.
  1684. * Might be faster to move from current position, but would need to know
  1685. * the current index.
  1686. */
  1687. STDMETHODIMP CTxtRange::SetIndex (
  1688. long Unit, //@parm Unit to index
  1689. long Index, //@parm Index value to use
  1690. long Extend) //@parm if nonzero, set range to <p Unit>
  1691. {
  1692. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetIndex");
  1693. if(IsZombie())
  1694. return CO_E_RELEASED;
  1695. if(!Index)
  1696. return E_INVALIDARG;
  1697. CCallMgr callmgr(GetPed());
  1698. LONG cchText = GetTextLength();
  1699. CTxtRange rg(GetPed()); // Create IP at cp = 0
  1700. if(Index > 0) // Index going forward First
  1701. Index--; // Unit is at start of story
  1702. else // Index from end of story
  1703. rg.Set(cchText, cchText); // selecting whole story
  1704. LONG cUnit;
  1705. HRESULT hr = rg.Mover(Unit, Index, &cUnit, MOVE_END);
  1706. if(FAILED(hr))
  1707. return hr;
  1708. if(Index != cUnit || rg.GetCp() == cchText) // No such index in story
  1709. return E_INVALIDARG;
  1710. rg._cch = 0; // Collapse at active end
  1711. // namely at cpMost
  1712. LONG cpMin, cpMost;
  1713. if(Extend) // Select Index'th Unit
  1714. rg.Expander(Unit, TRUE, NULL, &cpMin, &cpMost);
  1715. if(Set(rg.GetCp(), rg._cch)) // Something changed
  1716. {
  1717. Update(TRUE);
  1718. return NOERROR;
  1719. }
  1720. return S_FALSE;
  1721. }
  1722. /*
  1723. * CTxtRange::SetPara (pPara)
  1724. *
  1725. * @mfunc
  1726. * Set this range's paragraph attributes to those given by <p pPara>
  1727. * This method is a "Paragraph format painter".
  1728. *
  1729. * @rdesc
  1730. * HRESULT = (!pPara) ? E_INVALIDARG :
  1731. * (if success) ? NOERROR :
  1732. * (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
  1733. */
  1734. STDMETHODIMP CTxtRange::SetPara (
  1735. ITextPara * pPara) //@parm Paragraph object with desired paragraph
  1736. { // formatting
  1737. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetPara");
  1738. if(!pPara)
  1739. return E_INVALIDARG;
  1740. if(IsZombie())
  1741. return CO_E_RELEASED;
  1742. ITextPara * pParaApply = (ITextPara *) new CTxtPara(this);
  1743. if(!pParaApply)
  1744. return E_OUTOFMEMORY;
  1745. HRESULT hr;
  1746. if(*(LONG *)pParaApply == *(LONG *)pPara) // If same vtable, use
  1747. { // its _PF
  1748. hr = ParaFormatSetter(&((CTxtPara *)pPara)->_PF,
  1749. ((CTxtPara *)pPara)->_dwMask);
  1750. }
  1751. else // Else copy
  1752. hr = pParaApply->SetDuplicate(pPara); // to clone and apply
  1753. pParaApply->Release();
  1754. return hr;
  1755. }
  1756. /*
  1757. * CTxtRange::SetPoint (x, y, Type, Extend)
  1758. *
  1759. * @mfunc
  1760. * Select text at or up through (depending on <p Extend>) the point
  1761. * (<p x>, <p y>).
  1762. *
  1763. * @rdesc
  1764. * HRESULT = NOERROR
  1765. */
  1766. STDMETHODIMP CTxtRange::SetPoint (
  1767. long x, //@parm Horizontal coord of point to select
  1768. long y, //@parm Vertical coord of point to select
  1769. long Type, //@parm Defines the end to extend if Extend != 0.
  1770. long Extend) //@parm Whether to extend selection to point
  1771. {
  1772. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetPoint");
  1773. if(IsZombie())
  1774. return CO_E_RELEASED;
  1775. // Copy the ped locally once to save some indirections
  1776. CTxtEdit *ped = GetPed();
  1777. CCallMgr callmgr(ped);
  1778. if(Type != tomStart && Type != tomEnd)
  1779. return E_INVALIDARG;
  1780. if(!ped->fInplaceActive())
  1781. {
  1782. // If we aren't inplace active we can't get a DC to
  1783. // calculate the cp.
  1784. return OLE_E_NOT_INPLACEACTIVE;
  1785. }
  1786. // Convert (x, y) from screen coordinates to client coordinates
  1787. POINT pt = {x, y};
  1788. // Caller specifies screen coordinates?
  1789. if ( !(Type & tomClientCoord) )
  1790. if(!ped->TxScreenToClient(&pt))
  1791. return E_FAIL; // It is unexpected for this to happen
  1792. // Get cp for (x, y)
  1793. LONG cpSel = ped->_pdp->CpFromPoint(pt, NULL, NULL, NULL, TRUE);
  1794. if(cpSel == -1)
  1795. return E_FAIL; // It is highly unexpected for this to fail
  1796. // Extend range as requested
  1797. LONG cchForSel = 0;
  1798. if(Extend)
  1799. {
  1800. LONG cpMin, cpMost;
  1801. GetRange(cpMin, cpMost);
  1802. if(Type == tomStart)
  1803. cchForSel = cpSel - cpMin;
  1804. else
  1805. cchForSel = cpSel - cpMost;
  1806. }
  1807. // Update range
  1808. Set(cpSel, cchForSel);
  1809. return S_OK;
  1810. }
  1811. /*
  1812. * CTxtRange::SetRange (cp1, cp2)
  1813. *
  1814. * @mfunc
  1815. * Set this range's ends
  1816. *
  1817. * @rdesc
  1818. * HRESULT = (cp1 > cp2) ? E_INVALIDARG
  1819. * : (if change) ? NOERROR : S_FALSE
  1820. */
  1821. STDMETHODIMP CTxtRange::SetRange (
  1822. long cp1, //@parm Char position for Start end
  1823. long cp2) //@parm Char position for End end
  1824. {
  1825. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetRange");
  1826. if(IsZombie())
  1827. return CO_E_RELEASED;
  1828. CCallMgr callmgr(GetPed());
  1829. LONG cpMin, cpMost; // Save starting cp's for
  1830. // change determination
  1831. GetRange(cpMin, cpMost);
  1832. ValidateCp(cp1);
  1833. ValidateCp(cp2);
  1834. Set(cp2, cp2 - cp1);
  1835. GetRange(cp1, cp2); // See if either range end changed
  1836. if(cp1 != cpMin || cp2 != cpMost) // (independent of active end)
  1837. {
  1838. Update(TRUE); // Update selection
  1839. return NOERROR;
  1840. }
  1841. return S_FALSE;
  1842. }
  1843. /*
  1844. * CTxtRange::SetStart (cp)
  1845. *
  1846. * @mfunc
  1847. * Set this range's Start cp
  1848. *
  1849. * @rdesc
  1850. * HRESULT = (if change) ? NOERROR : S_FALSE
  1851. *
  1852. * @comm
  1853. * Note that setting this range's cpMin to <p cp> also sets cpMost to
  1854. * <p cp> if <p cp> > cpMost.
  1855. */
  1856. STDMETHODIMP CTxtRange::SetStart (
  1857. long cp) //@parm Desired new Start cp
  1858. {
  1859. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetStart");
  1860. if(IsZombie())
  1861. return CO_E_RELEASED;
  1862. LONG cpMost = GetCpMost();
  1863. ValidateCp(cp);
  1864. return SetRange(max(cpMost, cp), cp); // Active end is Start
  1865. }
  1866. /*
  1867. * CTxtRange::SetText (bstr)
  1868. *
  1869. * @mfunc
  1870. * Replace text in this range by that given by <p bstr>. If <p bstr>
  1871. * is NULL, delete text in range.
  1872. *
  1873. * @rdesc
  1874. * HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
  1875. * (if success) ? NOERROR : S_FALSE
  1876. */
  1877. STDMETHODIMP CTxtRange::SetText (
  1878. BSTR bstr) //@parm Text to replace text in this range by
  1879. {
  1880. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetText");
  1881. if(IsZombie())
  1882. return CO_E_RELEASED;
  1883. CCallMgr callmgr(GetPed());
  1884. if(WriteAccessDenied())
  1885. return E_ACCESSDENIED;
  1886. LONG cchNew = bstr ? SysStringLen(bstr) : 0;
  1887. _cch = Replacer(cchNew, (TCHAR *)bstr, RR_ITMZ_UNICODEBIDI); // Select the new text
  1888. _TEST_INVARIANT_
  1889. GetPed()->TxSetMaxToMaxText();
  1890. return _cch == cchNew ? NOERROR : S_FALSE;
  1891. }
  1892. /*
  1893. * CTxtRange::StartOf (Unit, Extend, pDelta)
  1894. *
  1895. * @mfunc
  1896. * Move this range end(s) to start of the first overlapping Unit in
  1897. * the range.
  1898. *
  1899. * @rdesc
  1900. * HRESULT = (if change) ? NOERROR :
  1901. * (if <p Unit> valid) ? S_FALSE : E_INVALIDARG
  1902. */
  1903. STDMETHODIMP CTxtRange::StartOf (
  1904. long Unit, //@parm Unit to use
  1905. long Extend, //@parm If true, leave other end alone
  1906. long * pDelta) //@parm Out parm to get count of chars that
  1907. // StartOf moved
  1908. {
  1909. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::StartOf");
  1910. CCallMgr callmgr(GetPed());
  1911. LONG cpMin;
  1912. HRESULT hr = Expander (Unit, Extend, pDelta, &cpMin, NULL);
  1913. if(hr == NOERROR)
  1914. Update(TRUE); // Update selection
  1915. return hr;
  1916. }
  1917. //---------------------- CTxtRange ITextSelection stubs -----------------------------
  1918. // Dummy CTxtRange routines to simplify CTxtSelection inheritance hierarchy
  1919. STDMETHODIMP CTxtRange::GetFlags (long * pFlags)
  1920. {
  1921. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFlags");
  1922. return E_NOTIMPL;
  1923. }
  1924. STDMETHODIMP CTxtRange::SetFlags (long Flags)
  1925. {
  1926. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFlags");
  1927. return E_NOTIMPL;
  1928. }
  1929. STDMETHODIMP CTxtRange::GetType (long * pType)
  1930. {
  1931. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetType");
  1932. return E_NOTIMPL;
  1933. }
  1934. STDMETHODIMP CTxtRange::MoveLeft (long Unit, long Count, long Extend, long * pDelta)
  1935. {
  1936. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Left");
  1937. return E_NOTIMPL;
  1938. }
  1939. STDMETHODIMP CTxtRange::MoveRight (long Unit, long Count, long Extend, long * pDelta)
  1940. {
  1941. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Right");
  1942. return E_NOTIMPL;
  1943. }
  1944. STDMETHODIMP CTxtRange::MoveUp (long Unit, long Count, long Extend, long * pDelta)
  1945. {
  1946. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Up");
  1947. return E_NOTIMPL;
  1948. }
  1949. STDMETHODIMP CTxtRange::MoveDown (long Unit, long Count, long Extend, long * pDelta)
  1950. {
  1951. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Down");
  1952. return E_NOTIMPL;
  1953. }
  1954. STDMETHODIMP CTxtRange::HomeKey (long Unit, long Extend, long * pDelta)
  1955. {
  1956. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::HomeKey");
  1957. return E_NOTIMPL;
  1958. }
  1959. STDMETHODIMP CTxtRange::EndKey (long Unit, long Extend, long * pDelta)
  1960. {
  1961. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::EndKey");
  1962. return E_NOTIMPL;
  1963. }
  1964. STDMETHODIMP CTxtRange::TypeText (BSTR bstr)
  1965. {
  1966. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::TypeText");
  1967. return E_NOTIMPL;
  1968. }
  1969. //--------------------- ITextRange Private Helper Methods -----------------------------
  1970. /*
  1971. * @doc INTERNAL
  1972. *
  1973. * CTxtRange::Collapser (bStart)
  1974. *
  1975. * @mfunc
  1976. * Internal routine to collapse this range into a degenerate point
  1977. * either at the the start (<p bStart> is nonzero or the end
  1978. * (<p bStart> = 0)
  1979. */
  1980. void CTxtRange::Collapser (
  1981. long bStart) //@parm Flag specifying end to collapse at
  1982. {
  1983. TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEEXTERN, "CTxtRange::Collapser");
  1984. if(bStart) // Collapse to Start
  1985. {
  1986. if(_cch > 0)
  1987. FlipRange(); // Move active end to range Start
  1988. }
  1989. else // Collapse to End
  1990. {
  1991. if(_cch < 0)
  1992. FlipRange(); // Move active end to range End
  1993. const LONG cchText = GetAdjustedTextLength();
  1994. if(GetCp() > cchText) // IP can't follow final CR
  1995. Set(cchText, 0); // so move it before
  1996. }
  1997. if(_cch)
  1998. _fMoveBack = bStart != 0;
  1999. _cch = 0; // Collapse this range
  2000. _fSelHasEOP = FALSE; // Insertion points don't have
  2001. _fSelHasCell = FALSE; // EOPs or Cells
  2002. if(_fSel) // Notify if selection changed
  2003. GetPed()->GetCallMgr()->SetSelectionChanged();
  2004. Update_iFormat(-1); // Make sure format is up to date
  2005. }
  2006. /*
  2007. * CTxtRange::Comparer(pRange)
  2008. *
  2009. * @mfunc
  2010. * helper function for CTxtRange::InRange() and IsEqual()
  2011. *
  2012. * @rdesc
  2013. * 0 if not same story or if this range isn't contained by <p pRange>;
  2014. * -1 if ranges are equal; 1 if this range wholely contained in
  2015. * <p pRange>.
  2016. *
  2017. * @comm
  2018. * Note that if this range is degenerate and *pRange is nondegenerate,
  2019. * this range is not included in *pRange if it's located at pRange's
  2020. * End position.
  2021. */
  2022. LONG CTxtRange::Comparer (
  2023. ITextRange * pRange) //@parm ITextRange to compare with
  2024. {
  2025. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Comparer");
  2026. LONG cpMin, cpMost;
  2027. LONG Start, End;
  2028. if(InStory(pRange, NULL) != NOERROR) // If this range doesn't point at
  2029. return 0; // same story as pRange's,
  2030. // return 0
  2031. GetRange(cpMin, cpMost); // Get this range's cp's
  2032. pRange->GetStart(&Start); // Get pRange's cp's
  2033. pRange->GetEnd(&End);
  2034. if(cpMin == Start && cpMost == End) // Exact match
  2035. return -1;
  2036. return cpMin >= Start && cpMost <= End && cpMin < End;
  2037. }
  2038. /*
  2039. * CTxtRange::Expander (Unit, fExtend, pDelta, pcpMin, pcpMost)
  2040. *
  2041. * @mfunc
  2042. * Helper function that expands this range so that partial Units it
  2043. * contains are completely contained according to the out parameters
  2044. * pcpMin and pcpMost. If pcpMin is not NULL, the range is expanded to
  2045. * the beginning of the Unit. Similarly, if pcpMost is not NULL, the
  2046. * range is expanded to the end of the Unit. <p pDelta> is an out
  2047. * parameter that receives the number of chars added to the range.
  2048. *
  2049. * @rdesc
  2050. * HRESULT = (if change) ? NOERROR :
  2051. * (if Unit valid) ? S_FALSE : E_INVALIDARG
  2052. *
  2053. * @devnote
  2054. * Used by ITextRange::Expand(), StartOf(), and EndOf(). Both pcpMin and
  2055. * pcpMost are nonNULL for Expand(). pcpMin is NULL for EndOf() and
  2056. * pcpMost is NULL for StartOf().
  2057. *
  2058. * @future
  2059. * Discontiguous Units. Expander should expand only to end of Unit,
  2060. * rather than to start of next Unit.
  2061. */
  2062. HRESULT CTxtRange::Expander (
  2063. long Unit, //@parm Unit to expand range to
  2064. BOOL fExtend, //@parm Expand this range if TRUE
  2065. LONG * pDelta, //@parm Out parm that receives chars added
  2066. LONG * pcpMin, //@parm Out parm that receives new cpMin
  2067. LONG * pcpMost) //@parm Out parm that receives new cpMost
  2068. {
  2069. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Expander");
  2070. if(IsZombie())
  2071. return CO_E_RELEASED;
  2072. LONG cch = 0; // Default no chars added
  2073. LONG cchRange;
  2074. LONG cchAdjustedText = GetAdjustedTextLength();
  2075. LONG cchText = GetTextLength();
  2076. LONG cp;
  2077. LONG cpMin, cpMost;
  2078. BOOL fUnitFound = TRUE; // Most Units can be found
  2079. LONG cchCollapse;
  2080. CDisplay *pdp; // but tomObject maybe not
  2081. GetRange(cpMin, cpMost); // Save starting cp's
  2082. if(pcpMin) // Default no change
  2083. {
  2084. *pcpMin = cpMin;
  2085. AssertSz(!pcpMost || fExtend,
  2086. "CTxtRange::Expander should extend if both pcpMin and pcpMost != 0");
  2087. }
  2088. if(pcpMost)
  2089. *pcpMost = cpMost;
  2090. if(pDelta)
  2091. *pDelta = 0;
  2092. if(Unit < 0)
  2093. {
  2094. // Valid attribute Units are high bit plus any combo of CFE_xxx.
  2095. // CFE_REVISED is most significant value currently defined.
  2096. if(Unit & ~(2*CFM_REVISED - 1 + 0x80000000))
  2097. return E_NOTIMPL;
  2098. FindAttributes(pcpMin, pcpMost, Unit);
  2099. }
  2100. else
  2101. {
  2102. switch(Unit) // Calculate new cp's
  2103. {
  2104. case tomObject:
  2105. fUnitFound = FindObject(pcpMin, pcpMost);
  2106. break;
  2107. case tomCharacter:
  2108. if (pcpMost && cpMin == cpMost &&// EndOf/Expand insertion point
  2109. cpMost < cchText && // with at least 1 more char
  2110. (!cpMost || pcpMin)) // at beginning of story or
  2111. { // Expand(), then
  2112. (*pcpMost)++; // expand by one char
  2113. }
  2114. break;
  2115. case tomCharFormat:
  2116. _rpCF.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
  2117. break;
  2118. case tomParaFormat:
  2119. _rpPF.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
  2120. break;
  2121. case tomWord:
  2122. FindWord (pcpMin, pcpMost, FW_INCLUDE_TRAILING_WHITESPACE);
  2123. break;
  2124. case tomSentence:
  2125. FindSentence (pcpMin, pcpMost);
  2126. break;
  2127. case tomCell:
  2128. FindCell (pcpMin, pcpMost);
  2129. break;
  2130. case tomRow:
  2131. _rpPF.AdjustForward();
  2132. if(InTable()) // In our simple table model,
  2133. goto para; // each row is a single para
  2134. break;
  2135. case tomLine:
  2136. pdp = GetPed()->_pdp;
  2137. if(pdp) // If this story has a display
  2138. { // use line array
  2139. CLinePtr rp(pdp);
  2140. cp = GetCp();
  2141. pdp->WaitForRecalc(cp, -1);
  2142. rp.RpSetCp(cp, FALSE);
  2143. rp.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
  2144. break;
  2145. } // Else fall thru to tomPara
  2146. case tomParagraph:
  2147. para: FindParagraph(pcpMin, pcpMost);
  2148. break;
  2149. case tomWindow:
  2150. fUnitFound = FindVisibleRange(pcpMin, pcpMost);
  2151. break;
  2152. case tomStory:
  2153. if(pcpMin)
  2154. *pcpMin = 0;
  2155. if(pcpMost)
  2156. *pcpMost = cchText;
  2157. break;
  2158. default:
  2159. return E_NOTIMPL;
  2160. }
  2161. }
  2162. if(!fUnitFound)
  2163. return S_FALSE;
  2164. cchCollapse = !fExtend && _cch; // Collapse counts as a char
  2165. // Note: Expand() has fExtend = 0
  2166. if(pcpMin)
  2167. {
  2168. cch = cpMin - *pcpMin; // Default positive cch for Expand
  2169. cpMin = *pcpMin;
  2170. }
  2171. if(pcpMost) // EndOf() and Expand()
  2172. {
  2173. if(!fExtend) // Will be IP if not already
  2174. {
  2175. if(cpMost > cchAdjustedText) // If we collapse (EndOf only),
  2176. cchCollapse = -cchCollapse; // it'll be before the final CR
  2177. else
  2178. *pcpMost = min(*pcpMost, cchAdjustedText);
  2179. }
  2180. cch += *pcpMost - cpMost;
  2181. cp = cpMost = *pcpMost;
  2182. }
  2183. else // StartOf()
  2184. {
  2185. cch = -cch; // Invert count
  2186. cp = cpMin; // Active end at cpMin
  2187. cchCollapse = -cchCollapse; // Backward collapses count as -1
  2188. }
  2189. cch += cchCollapse; // Collapse counts as a char
  2190. if(cch) // One or both ends changed
  2191. {
  2192. cchRange = cpMost - cpMin; // cch for EndOf() and Expand()
  2193. if(!pcpMost) // Make negative for StartOf()
  2194. cchRange = -cchRange;
  2195. if(!fExtend) // We're not expanding (EndOf()
  2196. cchRange = 0; // or StartOf() call)
  2197. if(Set(cp, cchRange)) // Set active end and signed cch
  2198. { // Something changed
  2199. if(pDelta) // Report cch if caller cares
  2200. *pDelta = cch;
  2201. return NOERROR;
  2202. }
  2203. }
  2204. return S_FALSE; // Report Unit found but no change
  2205. }
  2206. /*
  2207. * CTxtRange::Finder (bstr, Count, dwFlags, pDelta, Mode)
  2208. *
  2209. * @mfunc
  2210. * Helper find function that moves active end up to <p cch> characters
  2211. * subject to compare flags <p Flags> and the <p Mode>, which has the
  2212. * following possible values:
  2213. *
  2214. * 1: set this range's cpMost = cpMost of matched string
  2215. * 0: set this range's cp's equal to those of matched string
  2216. * -1: set this range's cpMin = cpMin of matched string
  2217. *
  2218. * Return *<p pDelta> = # characters past.
  2219. *
  2220. * @rdesc
  2221. * HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
  2222. *
  2223. * @devnote
  2224. * Used by ITextRange::FindText(), FindTextStart() and FindTextEnd()
  2225. */
  2226. HRESULT CTxtRange::Finder (
  2227. BSTR bstr, //@parm String to find
  2228. long Count, //@parm Max count of chars to search
  2229. long Flags, //@parm Flags governing compares
  2230. LONG * pDelta, //@parm Out parm to receive count of chars moved
  2231. MOVES Mode) //@parm Governs setting of range wrt matched string
  2232. {
  2233. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Finder");
  2234. if(!bstr)
  2235. return S_FALSE;
  2236. if(IsZombie())
  2237. return CO_E_RELEASED;
  2238. CCallMgr callmgr(GetPed());
  2239. LONG cpMin, cpMost;
  2240. LONG cch = GetRange(cpMin, cpMost); // Get this range's cp's
  2241. LONG cchBstr = SysStringLen(bstr);
  2242. LONG cchSave = _cch;
  2243. LONG cp, cpMatch, cpSave;
  2244. LONG cpStart = cpMost; // Default Start cp to range
  2245. CRchTxtPtr rtp(*this); // End
  2246. if(Mode == MOVE_IP) // FindText(): Count = 0 is
  2247. { // treated specially: if IP,
  2248. if(!Count) // compare string at IP; else
  2249. Count = cch ? cch : cchBstr; // confine search to range
  2250. if(Count > 0) // Forward searches start from
  2251. cpStart = cpMin; // beginning of range
  2252. }
  2253. else // FindTextStart() or
  2254. { // FindTextEnd()
  2255. if(!Count) // Compare string at IP; else
  2256. Count = cch ? -Mode*cch : cchBstr; // confine search to range
  2257. if(Mode < 0) // Find from Start
  2258. cpStart = cpMin;
  2259. }
  2260. cpSave = cpStart; // Save starting cp
  2261. cp = cpStart + Count; // cp = limiting cp. Can be on
  2262. cp = max(cp, 0); // either side of cpStart
  2263. Flags &= ~FR_DOWN; // Default search backward
  2264. if(Count >= 0) // It's forward, so set
  2265. Flags |= FR_DOWN; // downward search bit
  2266. find:
  2267. rtp.SetCp(cpStart); // Move to start of search
  2268. cpMatch = rtp.FindText(cp, Flags, bstr, cchBstr);
  2269. if (Mode == MOVE_IP && cpMatch == cpMin && // Ordinary Find matched
  2270. rtp.GetCp() == cpMost) // current range
  2271. {
  2272. Assert(cpStart == cpSave); // (Can't loop twice)
  2273. cpStart += Count > 0 ? 1 : -1; // Move over one char
  2274. goto find; // and try again
  2275. }
  2276. if(cpMatch < 0) // Match failed
  2277. {
  2278. if(pDelta) // Return match string length
  2279. *pDelta = 0; // = 0
  2280. return S_FALSE; // Signal no match
  2281. }
  2282. // Match succeeded: set new cp and cch for range, update selection (if
  2283. // this range is a selection), send notifications, and return NOERROR
  2284. cp = rtp.GetCp(); // cp = cpMost of match string
  2285. if(pDelta) // Return match string length
  2286. *pDelta = cchBstr; // if caller wants to know
  2287. cch = cp - cpMatch; // Default to select matched
  2288. // string (for MOVE_IP)
  2289. if(Mode != MOVE_IP) // MOVE_START or MOVE_END
  2290. {
  2291. if(Mode == MOVE_START) // MOVE_START moves to start
  2292. cp = cpMatch; // of matched string
  2293. cch = cp - cpSave; // Distance end moved
  2294. if(!cchSave && (Mode ^ cch) < 0) // If crossed ends of initial
  2295. cch = 0; // IP, use an IP
  2296. else if(cchSave) // Initially nondegenerate
  2297. { // range
  2298. if((cchSave ^ Mode) < 0) // If wrong end is active,
  2299. cchSave = -cchSave; // fake a FlipRange to get
  2300. cch += cchSave; // new length
  2301. if((cch ^ cchSave) < 0) // If ends would cross,
  2302. cch = 0; // convert to insertion point
  2303. }
  2304. }
  2305. if ((cp != GetCp() || cch != _cch) // Active end and/or length of
  2306. && Set(cp, cch)) // range changed
  2307. { // Use the new values
  2308. Update(TRUE); // Update selection
  2309. }
  2310. return NOERROR;
  2311. }
  2312. /*
  2313. * CTxtRange::Matcher (Cset, Count, pDelta, fExtend, Match)
  2314. *
  2315. * @mfunc
  2316. * Helper function to move active end up to <p cch> characters past
  2317. * all contiguous characters that are (<p Match> ? in : not in) the cset
  2318. * *<p pvar>. If <p fExtend>, extend the range to include the characters
  2319. * past by. Return *<p pDelta> = # characters past by.
  2320. *
  2321. * @rdesc
  2322. * HRESULT = (if change) ? NOERROR :
  2323. * (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
  2324. */
  2325. HRESULT CTxtRange::Matcher (
  2326. VARIANT * Cset, //@parm Character match set
  2327. long Count, //@parm Max cch to match
  2328. long * pDelta, //@parm Out parm for cch moved
  2329. MOVES Mode, //@parm MOVE_START (-1), MOVE_IP (0), MOVE_END (1)
  2330. MATCHES Match) //@parm MATCH_WHILE spans Cset; else break on Cset
  2331. {
  2332. #ifndef PEGASUS
  2333. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Matcher");
  2334. // This (and other) code assumes the following conditions:
  2335. Assert(MOVE_START == -1 && MOVE_IP == 0 && MOVE_END == 1);
  2336. Assert(MATCH_UNTIL == 0 && MATCH_WHILE == 1);
  2337. Assert(sizeof(WORD) == 2); // 16-bit WORDs
  2338. if(!Cset)
  2339. return E_INVALIDARG;
  2340. if(IsZombie())
  2341. return CO_E_RELEASED;
  2342. CCallMgr callmgr(GetPed());
  2343. LONG cch; // For cch moved
  2344. TCHAR ch; // Current char
  2345. LONG count = Count; // Count down variable
  2346. LONG cpSave; // To save initial cp
  2347. WORD ctype; // CT_TYPEx info for ch
  2348. long Delta; // Value for *pDelta
  2349. BOOL fInCset; // TRUE iff ch in Cset
  2350. UINT i, j; // Handy indices
  2351. LONG iDir = (Count > 0) ? 1 : -1; // Count increment
  2352. long lVal = Cset->lVal;
  2353. TCHAR * pch; // Used to walk BSTR Cset
  2354. CTxtPtr tp(_rpTX); // tp to walk text with
  2355. LONG vt = Cset->vt;
  2356. if(pDelta) // Default neither motion
  2357. *pDelta = 0; // nor match
  2358. if (Mode == MOVE_IP && (_cch ^ Count) < 0 || // Wrong active end:
  2359. Mode != MOVE_IP && (_cch ^ Mode) < 0)
  2360. {
  2361. tp.AdvanceCp(-_cch); // go to other end
  2362. }
  2363. cpSave = tp.GetCp(); // Save cp for checks
  2364. if(Count > 0) // If matching forward,
  2365. { // get current char
  2366. ch = tp.GetChar();
  2367. count--; // One less char to match
  2368. }
  2369. else // If matching backward,
  2370. ch = tp.NextCharCount(count); // start at previous char
  2371. if(!ch) // At one end or other, so
  2372. return S_FALSE; // signal no match
  2373. // Process built-in and explicit character sets
  2374. if(vt & VT_BYREF) // VB passes VT_BYREF
  2375. { // unless args are
  2376. lVal = *Cset->plVal; // enclosed in ()'s
  2377. vt &= ~ VT_BYREF;
  2378. }
  2379. if(vt == VT_I2) // Should be VT_I4, but
  2380. lVal &= 0xffff; // facilitate common cases
  2381. // Built-in char set: either Unicode range or CT_CTYPEx
  2382. if(vt == VT_I4 || vt == VT_I2)
  2383. {
  2384. i = lVal & 0xffff; // First code or CT mask
  2385. j = lVal >> 16; // Size of range
  2386. if(lVal < 0) // Unicode range Cset
  2387. { // (sign bit is set)
  2388. j &= 0x7fff; // Kill sign bit
  2389. while (((BOOL)Match ^ (ch - i > j)) && // ch in range or not
  2390. (ch = tp.NextCharCount(count))) // Another char available
  2391. ; // Note: count is passed
  2392. } // by reference
  2393. else // CT_CTYPEx Cset
  2394. { // CT_CTYPEx is given by
  2395. if(!j) // upper WORD of lVal
  2396. j = CT_CTYPE1; // 0 defaults to CT_CTYPE1
  2397. do
  2398. {
  2399. ctype = 0; // For each char, get
  2400. // string type info
  2401. W32->GetStringTypeEx(0, j, &ch, 1, &ctype);
  2402. // Loop (up to |Count| - 1 times) as long as the characters
  2403. // encountered are in the Cset (Match = MATCH_WHILE (=1)),
  2404. // or as long as they are not (Match = MATCH_UNTIL (=0)).
  2405. fInCset = (j == CT_CTYPE2) // CT_CTYPE2 values are
  2406. ? (ctype == i) // mutually exclusive;
  2407. : (ctype & i) != 0; // others can be combos
  2408. } while ((Match ^ fInCset) == 0 &&
  2409. (ch = tp.NextCharCount(count)) != 0);
  2410. } // End of built-in Csets
  2411. } // End of Cset VT_I4
  2412. // Explicit char set given by chars in Cset->bstrVal
  2413. else if (Cset->vt == VT_BSTR)
  2414. {
  2415. //REVIEW (keithcu) What is going on here?
  2416. if((DWORD_PTR)Cset->bstrVal < 0xfffff) // Don't get fooled by
  2417. return E_INVALIDARG; // invalid vt values
  2418. j = SysStringLen(Cset->bstrVal);
  2419. do
  2420. { // Set i = 0 if ch isn't
  2421. pch = Cset->bstrVal; // in set; this stops
  2422. for(i = j; // movement
  2423. i && (ch != *pch++);
  2424. i--) ;
  2425. // If we are doing a MATCH_WHILE routine then we only
  2426. // continue while i > 0 becuase this indicates that we
  2427. // found the char at the current cp in the CSet. If
  2428. // we were doing a MATCH_UNTIL then we should quit when
  2429. // i != 0 becuase the current char was in the CSet.
  2430. } while((Match == (i ? MATCH_WHILE : MATCH_UNTIL)) &&
  2431. (ch = tp.NextCharCount(count))); // Break if no more chars
  2432. } // or ch not in set
  2433. else
  2434. return E_INVALIDARG;
  2435. /* If MoveWhile, leave tp immediately after last matched char going
  2436. * forward and at that char going backward (helps to think of tp
  2437. * pointing in between chars). If MoveUntil, leave tp at the char
  2438. * going forward and just after that char going backward.
  2439. *
  2440. * E.g.: the code
  2441. *
  2442. * r.MoveUntil (C1_DIGIT, tomForward, NULL)
  2443. * r.MoveEndWhile(C1_DIGIT, tomForward, NULL)
  2444. *
  2445. * breaks at the first digit and selects the number going forward.
  2446. * Similarly
  2447. *
  2448. * r.MoveUntil (C1_DIGIT, tomBackward, NULL)
  2449. * r.MoveStartWhile(C1_DIGIT, tomBackward, NULL)
  2450. *
  2451. * selects the number going backward.
  2452. */
  2453. count = (Match == MATCH_WHILE && !ch) // If MoveWhile, move past
  2454. ? iDir : 0; // last matched char
  2455. if(Count < 0)
  2456. count++;
  2457. tp.AdvanceCp(count);
  2458. Delta = cch = 0; // Suppress motion unless
  2459. if(Match == MATCH_WHILE || ch) // match occurred
  2460. {
  2461. Delta = cch = tp.GetCp() - cpSave; // Calculate distance moved
  2462. if(Match == MATCH_UNTIL) // For MoveUntil methods,
  2463. Delta += iDir; // match counts as a char
  2464. }
  2465. if(pDelta) // Report motion to caller
  2466. *pDelta = Delta; // if it wants to know
  2467. // Handle cases for which range is changed
  2468. if(cch || (Delta && _cch && Mode == MOVE_IP))
  2469. {
  2470. if (Mode == MOVE_IP || // If move IP or asked to
  2471. !_cch && (Mode ^ Count) < 0) // cross ends of initial
  2472. { // IP, use an IP
  2473. cch = 0;
  2474. }
  2475. else if(_cch) // Initially nondegenerate
  2476. { // range
  2477. if((_cch ^ Mode) < 0) // If wrong end is active,
  2478. _cch = -_cch; // fake a FlipRange (will
  2479. cch += _cch; // set cp shortly)
  2480. if((cch ^ _cch) < 0) // If ends crossed, convert
  2481. cch = 0; // to insertion point
  2482. }
  2483. if(Set(tp.GetCp(), cch)) // Set new range cp and cch
  2484. {
  2485. Update(TRUE); // Update selection
  2486. return NOERROR; // Signal match occurred
  2487. }
  2488. return S_FALSE;
  2489. }
  2490. // No change in range. Return NOERROR iff match occurred for MOVE_UNTIL
  2491. return Delta ? NOERROR : S_FALSE;
  2492. #else
  2493. return 0;
  2494. #endif
  2495. }
  2496. /*
  2497. * CTxtRange::Mover (Unit, Count, pDelta, Mode)
  2498. *
  2499. * @mfunc
  2500. * Helper function to move end(s) <p Count> <p Unit>s, which end(s)
  2501. * depending on Mode = MOVE_IP, MOVE_START, and MOVE_END. Collapsing
  2502. * the range by using MOVE_IP counts as a Unit.
  2503. *
  2504. * Extends range from End if <p Mode> = MOVE_END and from Start if
  2505. * <p Mode> = MOVE_START; else (MOVE_IP) it collapses range to Start if
  2506. * <p Count> <lt>= 0 and to End if <p Count> <gt> 0.
  2507. *
  2508. * Sets *<p pDelta> = count of Units moved
  2509. *
  2510. * Used by ITextRange::Delete(), Move(), MoveStart(), MoveEnd(),
  2511. * and SetIndex()
  2512. *
  2513. * @rdesc
  2514. * HRESULT = (if change) ? NOERROR :
  2515. * (if <p Unit> valid) ? S_FALSE : E_INVALIDARG
  2516. */
  2517. HRESULT CTxtRange::Mover (
  2518. long Unit, //@parm Unit to use for moving active end
  2519. long Count, //@parm Count of units to move active end
  2520. long * pDelta, //@parm Out parm for count of units moved
  2521. MOVES Mode) //@parm MOVE_START (-1), MOVE_IP (0), MOVE_END (1)
  2522. {
  2523. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Mover");
  2524. if(pDelta)
  2525. *pDelta = 0; // Default no units moved
  2526. if(IsZombie())
  2527. return CO_E_RELEASED;
  2528. LONG cch;
  2529. LONG cchAdj = GetAdjustedTextLength();
  2530. LONG cchMax = 0; // Default full story limits
  2531. LONG cp;
  2532. LONG cpMost = GetCpMost();
  2533. LONG cUnitCollapse = 0;
  2534. HRESULT hr = NOERROR;
  2535. CTxtRange rg(*this); // Use a copy to look around
  2536. if(pDelta)
  2537. *pDelta = 0; // Default no units moved
  2538. if(_cch && Count) // Nondegenerate range
  2539. {
  2540. if(Mode == MOVE_IP) // Insertion point: will
  2541. { // collapse range if Unit is
  2542. if((Count ^ rg._cch) < 0) // defined. Go to correct end
  2543. rg.FlipRange();
  2544. if(Count > 0)
  2545. {
  2546. if(cpMost > cchAdj)
  2547. {
  2548. cUnitCollapse = -1; // Collapse before final CR
  2549. Count = 0; // No more motion
  2550. }
  2551. else
  2552. { // Extend pDelta pcpMin pcpMost
  2553. hr = rg.Expander(Unit, FALSE, NULL, NULL, &cp);
  2554. cUnitCollapse = 1; // Collapse counts as a Unit
  2555. Count--; // One less Unit to count
  2556. }
  2557. }
  2558. else
  2559. {
  2560. hr = rg.Expander(Unit, FALSE, NULL, &cp, NULL);
  2561. cUnitCollapse = -1;
  2562. Count++;
  2563. }
  2564. if(FAILED(hr))
  2565. return hr;
  2566. }
  2567. else if((Mode ^ rg._cch) < 0) // MOVE_START or MOVE_END
  2568. rg.FlipRange(); // Go to Start or End
  2569. }
  2570. if(Count > 0 && Mode != MOVE_END) // Moving IP or Start forward
  2571. {
  2572. cchMax = cchAdj - rg.GetCp(); // Can't pass final CR
  2573. if(cchMax <= 0) // Already at or past it
  2574. { // Only count comes from
  2575. Count = cUnitCollapse; // possible collapse
  2576. cp = cchAdj; // Put active end at cchAdj
  2577. cch = (Mode == MOVE_START && cpMost > cchAdj)
  2578. ? cp - cpMost : 0;
  2579. goto set;
  2580. }
  2581. }
  2582. cch = rg.UnitCounter(Unit, Count, cchMax); // Count off Count Units
  2583. if(cch == tomForward) // Unit not implemented
  2584. return E_NOTIMPL;
  2585. if(cch == tomBackward) // Unit not available, e.g.,
  2586. return S_FALSE; // tomObject and no objects
  2587. Count += cUnitCollapse; // Add a Unit if collapse
  2588. if(!Count) // Nothing changed, so quit
  2589. return S_FALSE;
  2590. if (Mode == MOVE_IP || // MOVE_IP or
  2591. !_cch && (Mode ^ Count) < 0) // initial IP end cross
  2592. {
  2593. cch = 0; // New range is degenerate
  2594. }
  2595. else if(_cch) // MOVE_START or MOVE_END
  2596. { // with nondegenerate range
  2597. if((_cch ^ Mode) < 0) // Make _cch correspond to end
  2598. _cch = -_cch; // that moved
  2599. cch += _cch; // Possible new range length
  2600. if((cch ^ _cch) < 0) // Nondegenerate end cross
  2601. cch = 0; // Use IP
  2602. }
  2603. cp = rg.GetCp();
  2604. set:
  2605. if(Set(cp, cch)) // Attempt to set new range
  2606. { // Something changed
  2607. if(pDelta) // Report count of units
  2608. *pDelta = Count; // advanced
  2609. Update(TRUE); // Update selection
  2610. return NOERROR;
  2611. }
  2612. return S_FALSE;
  2613. }
  2614. /*
  2615. *
  2616. * CTxtRange::Replacer (cchNew, *pch)
  2617. *
  2618. * @mfunc
  2619. * Replace this range's using CHARFORMAT _iFormat and updating other
  2620. * text runs as needed.
  2621. *
  2622. * Same as CTxtRange::CleanseAndReplaceRange(cchNew, *pch, publdr),
  2623. * except creates its own undo builder.
  2624. *
  2625. * @rdesc
  2626. * cch of text actually pasted
  2627. *
  2628. * @devnote
  2629. * moves this text pointer to end of replaced text and
  2630. * may move text block and formatting arrays
  2631. */
  2632. LONG CTxtRange::Replacer (
  2633. LONG cchNew, //@parm Length of replacement text
  2634. TCHAR const * pch, //@parm Replacement text
  2635. DWORD dwFlags) //@parm ReplaceRange flags
  2636. {
  2637. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Replacer");
  2638. IUndoBuilder * publdr;
  2639. CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
  2640. undobldr.StopGroupTyping();
  2641. // Note: we don't check the limit on text here. Right now, this
  2642. // is only called by Delete and SetText so this is OK. However,
  2643. // we might want to reinvestigate this latter if this is called
  2644. // by anything else.
  2645. return CleanseAndReplaceRange(cchNew, pch, FALSE, publdr, NULL, NULL, dwFlags);
  2646. }
  2647. /*
  2648. * CTxtRange::CharFormatSetter (pCF)
  2649. *
  2650. * @mfunc
  2651. * Helper function that's the same as CTxtRange::SetCharFormat(), but
  2652. * adds undo building, and notification.
  2653. *
  2654. * @rdesc
  2655. * HRESULT = (if success) ? NOERROR : S_FALSE
  2656. * (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
  2657. */
  2658. HRESULT CTxtRange::CharFormatSetter (
  2659. const CCharFormat *pCF, //@parm CCharFormat to fill with results
  2660. DWORD dwMask) //@parm CHARFORMAT2 mask
  2661. {
  2662. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::CharFormatSetter");
  2663. if(IsZombie())
  2664. return CO_E_RELEASED;
  2665. CTxtEdit *ped = GetPed();
  2666. CCallMgr callmgr(ped);
  2667. IUndoBuilder * publdr;
  2668. CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
  2669. if(WriteAccessDenied())
  2670. return E_ACCESSDENIED;
  2671. undobldr.StopGroupTyping();
  2672. return SetCharFormat(pCF, FALSE, publdr, dwMask, 0);
  2673. }
  2674. /*
  2675. * CTxtRange::ParaFormatSetter (pPF, dwMask)
  2676. *
  2677. * @mfunc
  2678. * Helper function that's the same as CTxtRange::SetParaFormat(), but
  2679. * adds protection checking, undo building, and notification.
  2680. *
  2681. * @rdesc
  2682. * HRESULT = (if success) ? NOERROR : S_FALSE
  2683. * (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
  2684. */
  2685. HRESULT CTxtRange::ParaFormatSetter (
  2686. const CParaFormat *pPF, //@parm CParaFormat to fill with results
  2687. DWORD dwMask) //@parm Mask to use
  2688. {
  2689. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::ParaFormatSetter");
  2690. if(IsZombie())
  2691. return CO_E_RELEASED;
  2692. CTxtEdit *ped = GetPed();
  2693. CCallMgr callmgr(ped);
  2694. IUndoBuilder * publdr;
  2695. CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
  2696. if(WriteAccessDenied())
  2697. return E_ACCESSDENIED;
  2698. undobldr.StopGroupTyping();
  2699. // In RichEdit 3.0, we don't support setting tables
  2700. if(GetPF()->InTable())
  2701. dwMask &= ~PFM_TABSTOPS;
  2702. return SetParaFormat(pPF, publdr, dwMask & ~PFM_TABLE);
  2703. }
  2704. /*
  2705. * CTxtRange::WriteAccessDenied()
  2706. *
  2707. * @mfunc
  2708. * Returns TRUE iff at least part of the range is protected and the
  2709. * owner chooses to enforce it
  2710. *
  2711. * @rdesc
  2712. * TRUE iff write access to range is denied
  2713. */
  2714. BOOL CTxtRange::WriteAccessDenied ()
  2715. {
  2716. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::WriteAccessDenied");
  2717. int iProt;
  2718. CTxtEdit *ped = GetPed();
  2719. if (ped && ped->TxGetReadOnly() ||
  2720. ((iProt = IsProtected(-1)) == PROTECTED_YES ||
  2721. (iProt == PROTECTED_ASK && ped->IsProtectionCheckingEnabled() &&
  2722. ped->QueryUseProtection(this, 0, 0, 0))))
  2723. // N.B. the preceding if statement assumes that IsProtected returns a tri-value
  2724. {
  2725. return TRUE;
  2726. }
  2727. return FALSE;
  2728. }
  2729. /*
  2730. * CTxtRange::IsTrue (f, pB)
  2731. *
  2732. * @mfunc
  2733. * Return *<p pB> = tomTrue iff <p f> is nonzero and pB isn't NULL
  2734. *
  2735. * @rdesc
  2736. * HRESULT = (f) ? NOERROR : S_FALSE
  2737. */
  2738. HRESULT CTxtRange::IsTrue(BOOL f, long *pB)
  2739. {
  2740. if(pB)
  2741. *pB = tomFalse;
  2742. if(IsZombie())
  2743. return CO_E_RELEASED;
  2744. if(f)
  2745. {
  2746. if(pB)
  2747. *pB = tomTrue;
  2748. return NOERROR;
  2749. }
  2750. return S_FALSE;
  2751. }
  2752. /*
  2753. * CTxtRange::GetLong (lValue, pLong)
  2754. *
  2755. * @mfunc
  2756. * Return *pLong = lValue provided pLong isn't NULL and this range
  2757. * isn't a zombie
  2758. *
  2759. * @rdesc
  2760. * HRESULT = (zombie) ? CO_E_RELEASED :
  2761. * (pLong) ? NOERROR : E_INVALIDARG
  2762. */
  2763. HRESULT CTxtRange::GetLong (
  2764. LONG lValue, //@parm Long value to return
  2765. long *pLong) //@parm Out parm to receive long value
  2766. {
  2767. if(IsZombie())
  2768. return CO_E_RELEASED;
  2769. _TEST_INVARIANT_
  2770. if(!pLong)
  2771. return E_INVALIDARG;
  2772. *pLong = lValue;
  2773. return NOERROR;
  2774. }
  2775. /*
  2776. * IsSameVtables (punk1, punk2)
  2777. *
  2778. * @mfunc
  2779. * Returns true if punk1 has same vtable as punk2
  2780. *
  2781. * @rdesc
  2782. * TRUE iff punk1 has same vtable as punk2
  2783. */
  2784. BOOL IsSameVtables(IUnknown *punk1, IUnknown *punk2)
  2785. {
  2786. return punk1 && punk2 && *(long *)punk1 == *(long *)punk2;
  2787. }
  2788. /*
  2789. * FPPTS_TO_TWIPS (x)
  2790. *
  2791. * @mfunc
  2792. * Returns 20*x, i.e., the number of twips corresponding to
  2793. * x given in floating-point points. The value is rounded.
  2794. *
  2795. * @rdesc
  2796. * x converted to twips
  2797. */
  2798. long FPPTS_TO_TWIPS(
  2799. float x)
  2800. {
  2801. return 20*x + ((x >= 0) ? 0.5 : -0.5);
  2802. }