Source code of Windows XP (NT5)
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.

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