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.

539 lines
15 KiB

  1. /*
  2. * @doc TOM
  3. *
  4. * @module tomsel.cpp - Implement the CTxtSelection Class |
  5. *
  6. * This module contains the TOM ITextSelection implementation for
  7. * the selection object
  8. *
  9. * History: <nl>
  10. * 5/24/95 - Alex Gounares: stubs <nl>
  11. * 8/95 - Murray Sargent: core implementation
  12. *
  13. * @comm
  14. * The "cursor-pad" functions (Left, Right, Up, Down, Home, End)
  15. * are simple generalizations of the corresponding keystrokes and have
  16. * to express the same UI. Consequently they are typically not as
  17. * efficient for moving the cursor around as ITextRange methods, which
  18. * are designed for particular purposes. This is especially true for
  19. * counts larger than one.
  20. *
  21. * @devnote
  22. * All ITextSelection methods inherited from ITextRange are handled by
  23. * the ITextRange methods, since they either don't affect the display of
  24. * the selection (e.g., Get methods), or virtual methods are used that
  25. * perform the appropriate updating of the selection on screen.
  26. *
  27. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  28. */
  29. #include "_common.h"
  30. #include "_select.h"
  31. #include "_disp.h"
  32. #include "_edit.h"
  33. #define DEBUG_CLASSNAME CTxtSelection
  34. #include "_invar.h"
  35. //---------------------- CTxtSelection methods ------------------------------------
  36. /*
  37. * CTxtSelection::EndKey (Unit, Extend, pDelta)
  38. *
  39. * @mfunc
  40. * Act as UI End key, such that <p Extend> is TRUE corresponds to the
  41. * Shift key being depressed and <p Unit> = start of line/document for
  42. * Ctrl key not being/being depressed. Returns *<p pDelta> = count of
  43. * characters active end is moved forward, i.e., a number >= 0.
  44. *
  45. * @rdesc
  46. * HRESULT = (invalid Unit) ? E_INVALIDARG :
  47. * (if change) ? NOERROR : S_FALSE
  48. */
  49. STDMETHODIMP CTxtSelection::EndKey (
  50. long Unit, //@parm Unit to use
  51. long Extend, //@parm Extend selection or go to IP
  52. long * pDelta) //@parm Out parm to receive count of chars moved
  53. {
  54. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::EndKey");
  55. return Homer(Unit, Extend, pDelta, End);
  56. }
  57. /*
  58. * CTxtSelection::GetFlags (pFlags)
  59. *
  60. * @mfunc
  61. * Set <p pFlags> = this text selection's flags
  62. *
  63. * @rdesc
  64. * HRESULT = (<p pFlags>) ? NOERROR : E_INVALIDARG
  65. */
  66. STDMETHODIMP CTxtSelection::GetFlags(
  67. long * pFlags) //@parm Out parm to receive selection flags
  68. {
  69. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetFlags");
  70. if(!pFlags)
  71. return E_INVALIDARG;
  72. if(IsZombie())
  73. {
  74. *pFlags = tomSelStartActive | tomSelReplace;
  75. return CO_E_RELEASED;
  76. }
  77. DWORD dwFlags = _cch <= 0; // Store tomSelStartActive value
  78. if(_fCaretNotAtBOL)
  79. dwFlags |= tomSelAtEOL;
  80. if(GetPed()->_fOverstrike)
  81. dwFlags |= tomSelOvertype;
  82. if(GetPed()->_fFocus)
  83. dwFlags |= tomSelActive;
  84. *pFlags = dwFlags | tomSelReplace; // tomSelReplace isn't optional
  85. return NOERROR;
  86. }
  87. /*
  88. * CTxtSelection::GetSelectionType (pType)
  89. *
  90. * @mfunc
  91. * Set *pType = type of this text selection
  92. *
  93. * @rdesc
  94. * HRESULT = <p pType> ? NOERROR : E_INVALIDARG
  95. */
  96. STDMETHODIMP CTxtSelection::GetType(
  97. long * pType) //@parm Out parm to receive selection type
  98. {
  99. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetSelectionType");
  100. if(!pType)
  101. return E_INVALIDARG;
  102. *pType = !_cch ? tomSelectionIP
  103. : (_cch == -1 && _rpTX.GetChar() == WCH_EMBEDDING ||
  104. _cch == 1 && GetPrevChar() == WCH_EMBEDDING)
  105. ? tomSelectionInlineShape : tomSelectionNormal;
  106. return IsZombie() ? CO_E_RELEASED : NOERROR;
  107. }
  108. /*
  109. * CTxtSelection::HomeKey (Unit, Extend, pDelta)
  110. *
  111. * @mfunc
  112. * Act as UI Home key, such that <p Extend> is TRUE corresponds to the
  113. * Shift key being depressed and <p Unit> = start of line/document for
  114. * Ctrl key not being/being depressed. Returns *<p pDelta> = count of
  115. * characters active end is moved forward, i.e., a number <= 0.
  116. *
  117. * @rdesc
  118. * HRESULT = (invalid Unit) ? E_INVALIDARG :
  119. * (if change) ? NOERROR : S_FALSE
  120. */
  121. STDMETHODIMP CTxtSelection::HomeKey (
  122. long Unit, //@parm Unit to use
  123. long Extend, //@parm Extend selection or go to IP
  124. long * pDelta) //@parm Out parm to receive count of chars moved
  125. {
  126. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::HomeKey");
  127. return Homer(Unit, Extend, pDelta, Home);
  128. }
  129. /*
  130. * CTxtSelection::MoveDown (Unit, Count, Extend, pDelta)
  131. *
  132. * @mfunc
  133. * Act as UI Down arrow, such that <p Extend> is TRUE corresponds to the
  134. * Shift key being depressed and <p Unit> = tomLine/tomParagraph for
  135. * Ctrl key not being/being depressed. In addition, <p Unit> can equal
  136. * tomWindow/tomWindowEnd for the Ctrl key not being/being depressed.
  137. * This second pair emulates PgDn behavior. The method returns
  138. * *<p pDelta> = actual count of units moved.
  139. *
  140. * @rdesc
  141. * HRESULT = (if change) ? NOERROR : S_FALSE
  142. */
  143. STDMETHODIMP CTxtSelection::MoveDown (
  144. long Unit, //@parm Unit to use
  145. long Count, //@parm Number of Units to move
  146. long Extend, //@parm Extend selection or go to IP
  147. long * pDelta) //@parm Out parm to receive actual count of
  148. // Units moved
  149. {
  150. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveDown");
  151. return GeoMover(Unit, Count, Extend, pDelta, 3);
  152. }
  153. /*
  154. * CTxtSelection::MoveLeft (Unit, Count, Extend, pDelta)
  155. *
  156. * @mfunc
  157. * Act as UI left arrow, such that <p Extend> is TRUE corresponds to the
  158. * Shift key being depressed and <p Unit> = tomChar/tomWord for Ctrl key
  159. * not being/being depressed. Returns *<p pDelta> = actual count of
  160. * units moved
  161. *
  162. * @rdesc
  163. * HRESULT = (if change) ? NOERROR : S_FALSE
  164. */
  165. STDMETHODIMP CTxtSelection::MoveLeft (
  166. long Unit, //@parm Unit to use
  167. long Count, //@parm Number of Units to move
  168. long Extend, //@parm Extend selection or go to IP
  169. long * pDelta) //@parm Out parm to receive actual count of
  170. // Units moved
  171. {
  172. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveLeft");
  173. return GeoMover(Unit, Count, Extend, pDelta, 0);
  174. }
  175. /*
  176. * CTxtSelection::MoveRight (Unit, Count, Extend, pDelta)
  177. *
  178. * @mfunc
  179. * Act as UI right arrow, such that <p Extend> is TRUE corresponds to the
  180. * Shift key being depressed and <p Unit> = tomChar/tomWord for Ctrl key
  181. * not being/being depressed. Returns *<p pDelta> = actual count of
  182. * units moved
  183. *
  184. * @rdesc
  185. * HRESULT = (if change) ? NOERROR : S_FALSE
  186. */
  187. STDMETHODIMP CTxtSelection::MoveRight (
  188. long Unit, //@parm Unit to use
  189. long Count, //@parm Number of Units to move
  190. long Extend, //@parm Extend selection or go to IP
  191. long * pDelta) //@parm Out parm to receive actual count of
  192. // Units moved
  193. {
  194. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveRight");
  195. return GeoMover(Unit, Count, Extend, pDelta, 1);
  196. }
  197. /*
  198. * CTxtSelection::MoveUp (Unit, Count, Extend, pDelta)
  199. *
  200. * @mfunc
  201. * Act as UI Up arrow, such that <p Extend> is TRUE corresponds to the
  202. * Shift key being depressed and <p Unit> = tomLine/tomParagraph for
  203. * Ctrl key not being/being depressed. In addition, <p Unit> can equal
  204. * tomWindow/tomWindowEnd for the Ctrl key not being/being depressed.
  205. * This second pair emulates PgUp behavior. The method returns
  206. * *<p pDelta> = actual count of units moved.
  207. *
  208. * @rdesc
  209. * HRESULT = (if change) ? NOERROR : S_FALSE
  210. */
  211. STDMETHODIMP CTxtSelection::MoveUp (
  212. long Unit, //@parm Unit to use
  213. long Count, //@parm Number of Units to move
  214. long Extend, //@parm Extend selection or go to IP
  215. long * pDelta) //@parm Out parm to receive actual count of
  216. // Units moved
  217. {
  218. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveUp");
  219. return GeoMover(Unit, Count, Extend, pDelta, 2);
  220. }
  221. /*
  222. * CTxtSelection::SetFlags (Flags)
  223. *
  224. * @mfunc
  225. * Set this text selection's flags = Flags
  226. *
  227. * @rdesc
  228. * HRESULT = NOERROR
  229. *
  230. * @comm
  231. * RichEdit ignores tomSelReplace since it's always on
  232. */
  233. STDMETHODIMP CTxtSelection::SetFlags(
  234. long Flags) //@parm New flag values
  235. {
  236. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::SetFlags");
  237. if(IsZombie())
  238. return CO_E_RELEASED;
  239. _fCaretNotAtBOL = (Flags & tomSelAtEOL) != 0;
  240. GetPed()->_fOverstrike = (Flags & tomSelOvertype) != 0;
  241. if(!(Flags & tomSelStartActive) ^ (_cch > 0))
  242. FlipRange();
  243. if((Flags & tomSelActive) && !GetPed()->_fFocus)
  244. GetPed()->TxSetFocus();
  245. return NOERROR;
  246. }
  247. /*
  248. * CTxtRange::SetPoint (x, y, Extend)
  249. *
  250. * @mfunc
  251. * Select text at or up through (depending on <p Extend>) the point
  252. * (<p x>, <p y>).
  253. *
  254. * @rdesc
  255. * HRESULT = NOERROR
  256. */
  257. STDMETHODIMP CTxtSelection::SetPoint (
  258. long x, //@parm Horizontal coord of point to select
  259. long y, //@parm Vertical coord of point to select
  260. long Extend) //@parm Whether to extend selection to point
  261. {
  262. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::SelectPoint");
  263. if(IsZombie())
  264. return CO_E_RELEASED;
  265. CCallMgr callmgr(GetPed());
  266. POINT ptxy = {x, y};
  267. POINTUV pt;
  268. _pdp->PointuvFromPoint(pt, ptxy);
  269. if(Extend)
  270. ExtendSelection (pt);
  271. else
  272. SetCaret(pt, FALSE);
  273. return NOERROR;
  274. }
  275. /*
  276. * CTxtSelection::TypeText (bstr)
  277. *
  278. * @mfunc
  279. * Type the string given by bstr at this selection as if someone typed it.
  280. * This is similar to the underlying ITextRange::SetText() method, but is
  281. * sensitive to the Ins/Ovr key state.
  282. *
  283. * @rdesc
  284. * HRESULT = !<p bstr> ? E_INVALIDARG :
  285. * (whole string typed) ? NOERROR : S_FALSE
  286. * @comm
  287. * This is faster than sending chars via SendMessage(), but it's slower
  288. * than using ITextRange::SetText()
  289. */
  290. STDMETHODIMP CTxtSelection::TypeText (
  291. BSTR bstr) //@parm String to type into this selection
  292. {
  293. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::TypeText");
  294. if(!bstr)
  295. return E_INVALIDARG;
  296. if(IsZombie())
  297. return CO_E_RELEASED;
  298. CCallMgr callmgr(GetPed());
  299. if(!GetPed()->IsntProtectedOrReadOnly(WM_CHAR, 0, 0))
  300. return E_ACCESSDENIED;
  301. CFreezeDisplay fd(_pdp);
  302. DWORD dwFlags = GetPed()->_fOverstrike;
  303. DWORD dwFlagsPutChar;
  304. OLECHAR * pch = bstr;
  305. IUndoBuilder * publdr;
  306. CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
  307. if(GetPed()->_fIMEInProgress) // Suppress autocorrect until last
  308. dwFlags |= KBD_NOAUTOCORRECT | KBD_CHAR; // character during IME
  309. dwFlagsPutChar = dwFlags;
  310. for(LONG cch = SysStringLen(bstr); cch > 0; dwFlags = dwFlagsPutChar)
  311. {
  312. unsigned ch = *pch++;
  313. cch--;
  314. if(IN_RANGE(0xD800, ch, 0xDBFF) && cch && IN_RANGE(0xDC00, *pch, 0xDFFF))
  315. {
  316. ch = (*pch++ & 0x3FF) + ((ch & 0x3FF) << 10) + 0x10000;
  317. cch--;
  318. dwFlags &= ~KBD_CHAR; // Need font binding
  319. }
  320. else if ((IN_RANGE(0x03400, ch, 0x04DFF) || IN_RANGE(0xE000, ch, 0x0F8FF)))
  321. dwFlags &= ~KBD_CHAR; // Need font binding
  322. if(!cch) // ch is last character: allow autocorrect
  323. dwFlags &= ~KBD_NOAUTOCORRECT;
  324. if(!PutChar(ch, dwFlags, publdr))
  325. break;
  326. undobldr.Done(); // Simulate one char input at a time
  327. }
  328. return cch ? S_FALSE : NOERROR;
  329. }
  330. //--------------------- ITextSelection PRIVATE helper methods -----------------------------
  331. /*
  332. * @doc INTERNAL
  333. *
  334. * CTxtSelection::GeoMover (Unit, Count, Extend, pDelta, iDir)
  335. *
  336. * @mfunc
  337. * Helper function to move active end <p Count> <p Unit>s geometrically
  338. *
  339. * Extends range if <p Extend> is TRUE; else collapses range to Start if
  340. * <p Count> <lt> 0 and to End if <p Count> <gt> 0.
  341. *
  342. * Sets *<p pDelta> = count of Units moved
  343. *
  344. * Used by ITextSelection::Left(), Right(), Up(), and Down()
  345. *
  346. * @rdesc
  347. * HRESULT = (if change) ? NOERROR : (if Unit supported) ? S_FALSE
  348. * : E_NOTIMPL
  349. */
  350. HRESULT CTxtSelection::GeoMover (
  351. long Unit, //@parm Unit to use
  352. long Count, //@parm Number of Units to move
  353. long Extend, //@parm Extend selection or go to IP
  354. long * pDelta, //@parm Out parm to receive count of Units moved
  355. LONG iDir) //@parm Direction to move in if Count > 0
  356. {
  357. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtSelection::GeoMover");
  358. if(pDelta) // Default no movement
  359. *pDelta = 0;
  360. if(IsZombie())
  361. return CO_E_RELEASED;
  362. CCallMgr callmgr(GetPed());
  363. LONG CountSave = Count;
  364. LONG cp;
  365. LONG cUnit;
  366. LONG iDefUnit = (iDir & 0xfe) == 2 ? tomLine : tomCharacter;
  367. BOOL fCollapse = !Extend && _cch;
  368. BOOL fCtrl = Unit != iDefUnit;
  369. BOOL fExtend = Extend != 0;
  370. if(Count < 0)
  371. {
  372. Count = -Count;
  373. iDir ^= 1;
  374. }
  375. if(iDefUnit == tomLine) // Up or Down
  376. {
  377. if(Unit == tomPage && GetPed()->IsInPageView())
  378. Unit = tomScreen;
  379. if(Unit == tomScreen)
  380. {
  381. iDir ^= 6; // Convert Up/Down to PgUp/PgDn
  382. fCtrl = FALSE;
  383. }
  384. else if(Unit == tomWindow) // Go to top/bottom of window
  385. {
  386. iDir ^= 6; // Convert Up/Down to PgUp/PgDn
  387. Count = 1; // Be sure Count = 1
  388. } // Leave fCtrl = 1
  389. else if(fCtrl && Unit != tomParagraph)
  390. return E_INVALIDARG;
  391. }
  392. else if(fCtrl && Unit != tomWord)
  393. return E_INVALIDARG;
  394. for (cUnit = Count; Count; Count--)
  395. {
  396. cp = GetCp(); // Save cp for comparison
  397. switch(iDir) // iDir bit 0 inc/dec for 1/0
  398. { // iDir values are chosen contiguously
  399. case 0: // to encourage compiler to use a
  400. Left(fCtrl, fExtend); // jump table
  401. break;
  402. case 1: // tomCharacter/tomWord OK here
  403. Right(fCtrl, fExtend);
  404. break;
  405. case 2: // tomLine/tomParagraph OK here
  406. Up(fCtrl, fExtend);
  407. break;
  408. case 3: // tomLine/tomParagraph OK here
  409. Down(fCtrl, fExtend);
  410. break;
  411. case 4: // tomWindow/tomScreen OK here
  412. PageUp(fCtrl, fExtend);
  413. break;
  414. case 5: // tomWindow/tomScreen OK here
  415. PageDown(fCtrl, fExtend);
  416. }
  417. if(cp == GetCp() && !fCollapse) // Didn't move or collapse
  418. break; // so we're done
  419. fCollapse = FALSE; // Collapse counts as a Unit
  420. }
  421. cUnit -= Count; // Count of Units moved
  422. if(CountSave < 0)
  423. cUnit = -cUnit; // Negative Counts get negative results
  424. if(pDelta)
  425. *pDelta = cUnit;
  426. return cUnit ? NOERROR : S_FALSE;
  427. }
  428. /*
  429. * CTxtSelection::Homer (Unit, Extend, pDelta, pfn)
  430. *
  431. * @mfunc
  432. * Helper function to move active end Home or End depending on pfn
  433. *
  434. * Extends range if <p Extend> is TRUE; else collapses range to Start if
  435. * <p Count> <lt> 0 and to End if <p Count> <gt> 0.
  436. *
  437. * Sets *<p pDelta> = count of chars moved forward
  438. *
  439. * Used by ITextSelection::Home(), End()
  440. *
  441. * @rdesc
  442. * HRESULT = (invalid Unit) ? E_INVALIDARG :
  443. * (if change) ? NOERROR : S_FALSE
  444. */
  445. HRESULT CTxtSelection::Homer (
  446. long Unit, //@parm Unit to use
  447. long Extend, //@parm Extend selection or go to IP
  448. long * pDelta, //@parm Out parm to receive count of Units moved
  449. BOOL (CTxtSelection::*pfn)(BOOL, BOOL)) //@parm Direction to move in
  450. {
  451. TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtSelection::Homer");
  452. if(pDelta) // Default no movement
  453. *pDelta = 0;
  454. if(IsZombie())
  455. return CO_E_RELEASED;
  456. if(Unit != tomLine && Unit != tomStory)
  457. return E_INVALIDARG;
  458. CCallMgr callmgr(GetPed());
  459. LONG cch = GetCp();
  460. (this->*pfn)(Unit != tomLine, Extend != 0);
  461. cch = GetCp() - cch;
  462. if(pDelta)
  463. *pDelta = cch;
  464. return cch ? NOERROR : S_FALSE;
  465. }