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.

7196 lines
180 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module EDIT.C - main part of CTxtEdit |
  5. *
  6. * See also textserv.cpp (ITextServices and SendMessage interfaces)
  7. * and tomDoc.cpp (ITextDocument interface)
  8. *
  9. * Authors: <nl>
  10. * Original RichEdit code: David R. Fulmer <nl>
  11. * Christian Fortini, Murray Sargent, Alex Gounares, Rick Sailor,
  12. * Jon Matousek
  13. *
  14. * History: <nl>
  15. * 12/28/95 jonmat-Added support of Magellan mouse and smooth scrolling.
  16. *
  17. * @devnote
  18. * Be sure to set tabs at every four (4) columns. In fact, don't even
  19. * think of doing anything else!
  20. *
  21. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  22. */
  23. #include "_common.h"
  24. #include "_edit.h"
  25. #include "_dispprt.h"
  26. #include "_dispml.h"
  27. #include "_dispsl.h"
  28. #include "_select.h"
  29. #include "_text.h"
  30. #include "_runptr.h"
  31. #include "_font.h"
  32. #include "_measure.h"
  33. #include "_render.h"
  34. #include "_m_undo.h"
  35. #include "_antievt.h"
  36. #include "_rtext.h"
  37. #include "_hyph.h"
  38. #include "_uspi.h"
  39. #include "_urlsup.h"
  40. #ifndef NOLINESERVICES
  41. #include "_ols.h"
  42. #endif
  43. #include "_txtbrk.h"
  44. #include "_clasfyc.h"
  45. #define CONTROL(_ch) (_ch - 'A' + 1)
  46. ASSERTDATA
  47. // This is not public because we don't really want folks using it.
  48. // ITextServices is a private interface.
  49. EXTERN_C const IID IID_ITextServices = { // 8d33f740-cf58-11ce-a89d-00aa006cadc5
  50. 0x8d33f740,
  51. 0xcf58,
  52. 0x11ce,
  53. {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
  54. };
  55. // {13E670F4-1A5A-11cf-ABEB-00AA00B65EA1}
  56. EXTERN_C const GUID IID_ITextHost =
  57. { 0x13e670f4, 0x1a5a, 0x11cf, { 0xab, 0xeb, 0x0, 0xaa, 0x0, 0xb6, 0x5e, 0xa1 } };
  58. // {13E670F5-1A5A-11cf-ABEB-00AA00B65EA1}
  59. EXTERN_C const GUID IID_ITextHost2 =
  60. { 0x13e670f5, 0x1a5a, 0x11cf, { 0xab, 0xeb, 0x0, 0xaa, 0x0, 0xb6, 0x5e, 0xa1 } };
  61. // this is used internally do tell if a data object is one of our own.
  62. EXTERN_C const GUID IID_IRichEditDO =
  63. { /* 21bc3b20-e5d5-11cf-93e1-00aa00b65ea1 */
  64. 0x21bc3b20,
  65. 0xe5d5,
  66. 0x11cf,
  67. {0x93, 0xe1, 0x00, 0xaa, 0x00, 0xb6, 0x5e, 0xa1}
  68. };
  69. // Static data members
  70. DWORD CTxtEdit::_dwTickDblClick; // time of last double-click
  71. POINT CTxtEdit::_ptDblClick; // position of last double-click
  72. //HCURSOR CTxtEdit::_hcurCross = 0; // We don't implement outline drag move
  73. HCURSOR CTxtEdit::_hcurArrow = 0;
  74. HCURSOR CTxtEdit::_hcurHand = 0;
  75. HCURSOR CTxtEdit::_hcurIBeam = 0;
  76. HCURSOR CTxtEdit::_hcurItalic = 0;
  77. HCURSOR CTxtEdit::_hcurSelBar = 0;
  78. HCURSOR CTxtEdit::_hcurVIBeam = 0;
  79. HCURSOR CTxtEdit::_hcurVItalic = 0;
  80. const WCHAR szCRLF[]= TEXT("\r\n");
  81. const WCHAR szCR[] = TEXT("\r");
  82. WORD g_wFlags = 0; // Keyboard controlled flags
  83. /*
  84. * GetKbdFlags(vkey, dwFlags)
  85. *
  86. * @func
  87. * return bit mask (RSHIFT, LSHIFT, RCTRL, LCTRL, RALT, or LALT)
  88. * corresponding to vkey = VK_SHIFT, VK_CONTROL, or VK_MENU and
  89. * dwFlags
  90. *
  91. * @rdesc
  92. * Bit mask corresponding to vkey and dwFlags
  93. */
  94. DWORD GetKbdFlags(
  95. WORD vkey, //@parm Virtual key code
  96. DWORD dwFlags) //@parm lparam of WM_KEYDOWN msg
  97. {
  98. if(vkey == VK_SHIFT)
  99. return (LOBYTE(HIWORD(dwFlags)) == 0x36) ? RSHIFT : LSHIFT;
  100. if(vkey == VK_CONTROL)
  101. return (HIWORD(dwFlags) & KF_EXTENDED) ? RCTRL : LCTRL;
  102. Assert(vkey == VK_MENU);
  103. return (HIWORD(dwFlags) & KF_EXTENDED) ? RALT : LALT;
  104. }
  105. LONG TwipsToHalfPoints(
  106. LONG x)
  107. {
  108. return x > 0 ? (x + 5)/10 : 0; // Convert twips to half points
  109. }
  110. LONG TwipsToQuarterPoints(
  111. LONG x)
  112. {
  113. return x > 0 ? (x + 3)/5 : 0; // Convert twips to quarter points
  114. }
  115. LONG CheckTwips(
  116. LONG x)
  117. {
  118. return x > 0 ? min(x, 255) : 0;
  119. }
  120. ///////////////// CTxtEdit Creation, Initialization, Destruction ///////////////////////////////////////
  121. /*
  122. * CTxtEdit::CTxtEdit()
  123. *
  124. * @mfunc
  125. * constructor
  126. */
  127. CTxtEdit::CTxtEdit(
  128. ITextHost2 *phost,
  129. IUnknown * punk)
  130. {
  131. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CTxtEdit");
  132. _unk.Init();
  133. _punk = (punk) ? punk : &_unk;
  134. _ldte.Init(this);
  135. _phost = phost;
  136. _cpAccelerator = -1; // Default to no accelerator
  137. // Initialize _iCF and _iPF to something bogus
  138. Set_iCF(-1);
  139. Set_iPF(-1);
  140. // Initialize local maximum text size to window default
  141. _cchTextMost = cInitTextMax;
  142. // This actually counts the number of active ped
  143. W32->AddRef();
  144. }
  145. /*
  146. * CTxtEdit::~CTxtEdit()
  147. *
  148. * @mfunc
  149. * Destructor
  150. */
  151. CTxtEdit::~CTxtEdit ()
  152. {
  153. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::~CTxtEdit");
  154. Assert(!_fMButtonCapture); // Need to properly transition
  155. // Magellan mouse if asserts!
  156. _fSelfDestruct = TRUE; // Tell the Call Mgr not to
  157. // call this any more
  158. // Flush clipboard first
  159. _ldte.FlushClipboard();
  160. if(_pDocInfo) // Do this before closing
  161. { // down internal structures
  162. CloseFile(TRUE); // Close any open file
  163. delete _pDocInfo; // Delete document info
  164. _pDocInfo = NULL;
  165. }
  166. if(_pdetecturl)
  167. delete _pdetecturl;
  168. #ifndef NOCOMPLEXSCRIPTS
  169. if (_pbrk)
  170. delete _pbrk;
  171. #endif
  172. if(_pobjmgr)
  173. delete _pobjmgr;
  174. // Release our reference to selection object
  175. if(_psel)
  176. _psel->Release();
  177. // Delete undo and redo managers
  178. if(_pundo)
  179. _pundo->Destroy();
  180. #ifndef NOPRIVATEMESSAGE
  181. if (_pMsgNotify)
  182. delete _pMsgNotify;
  183. #endif
  184. // Release message filter.
  185. // Note that the attached message filter must have released this document
  186. // Otherwise we will never get here.
  187. if (_pMsgFilter)
  188. {
  189. _pMsgFilter->Release();
  190. _pMsgFilter = 0;
  191. }
  192. if(_predo)
  193. _predo->Destroy();
  194. ReleaseFormats(Get_iCF(), Get_iPF()); // Release default formats
  195. delete _pdp; // Delete displays
  196. _pdp = NULL; // Break any further attempts to
  197. // use display
  198. delete _pdpPrinter;
  199. if (_fHost2)
  200. {
  201. // We are in a windows host - need to deal with the shutdown
  202. // problem where the window can be destroyed before text
  203. // services is.
  204. if (!_fReleaseHost)
  205. {
  206. ((ITextHost2*)_phost)->TxFreeTextServicesNotification();
  207. }
  208. else
  209. {
  210. // Had to keep host alive so tell it we are done with it.
  211. _phost->Release();
  212. }
  213. }
  214. W32->Release();
  215. }
  216. void CTxtEdit::TxInvalidateRect(const RECT *prc)
  217. {
  218. _phost->TxInvalidateRect(prc, FALSE);
  219. }
  220. void CTxtEdit::TxInvalidateRect(const RECTUV *prcuv)
  221. {
  222. CMeasurer me(_pdp);
  223. RECT rc;
  224. _pdp->RectFromRectuv(rc, *prcuv);
  225. _phost->TxInvalidateRect(&rc, FALSE);
  226. }
  227. /*
  228. * CTxtEdit::Init (prcClient)
  229. *
  230. * @mfunc
  231. * Initializes this CTxtEdit. Called by CreateTextServices()
  232. *
  233. * @rdesc
  234. * Return TRUE if successful
  235. */
  236. BOOL CTxtEdit::Init (
  237. const RECT *prcClient) //@parm Client RECT
  238. {
  239. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::Init");
  240. CCharFormat CF;
  241. DWORD dwBits = 0;
  242. DWORD dwMask;
  243. LONG iCF, iPF;
  244. CParaFormat PF;
  245. CCallMgr callmgr(this);
  246. static BOOL fOnce = FALSE;
  247. if (!fOnce)
  248. {
  249. CLock lock;
  250. fOnce = TRUE;
  251. _fnpPropChg[ 0] = &CTxtEdit::OnRichEditChange; // TXTBIT_RICHTEXT
  252. _fnpPropChg[ 1] = &CTxtEdit::OnTxMultiLineChange; // TXTBIT_MULTILINE
  253. _fnpPropChg[ 2] = &CTxtEdit::OnTxReadOnlyChange; // TXTBIT_READONLY
  254. _fnpPropChg[ 3] = &CTxtEdit::OnShowAccelerator; // TXTBIT_SHOWACCELERATOR
  255. _fnpPropChg[ 4] = &CTxtEdit::OnUsePassword; // TXTBIT_USEPASSWORD
  256. _fnpPropChg[ 5] = &CTxtEdit::OnTxHideSelectionChange; // TXTBIT_HIDESELECTION
  257. _fnpPropChg[ 6] = &CTxtEdit::OnSaveSelection; // TXTBIT_SAVESELECTION
  258. _fnpPropChg[ 7] = &CTxtEdit::OnAutoWordSel; // TXTBIT_AUTOWORDSEL
  259. _fnpPropChg[ 8] = &CTxtEdit::OnTxVerticalChange; // TXTBIT_VERTICAL
  260. _fnpPropChg[ 9] = &CTxtEdit::NeedViewUpdate; // TXTBIT_SELECTIONBAR
  261. _fnpPropChg[10] = &CTxtEdit::OnWordWrapChange; // TXTBIT_WORDWRAP
  262. _fnpPropChg[11] = &CTxtEdit::OnAllowBeep; // TXTBIT_ALLOWBEEP
  263. _fnpPropChg[12] = &CTxtEdit::OnDisableDrag; // TXTBIT_DISABLEDRAG
  264. _fnpPropChg[13] = &CTxtEdit::NeedViewUpdate; // TXTBIT_VIEWINSETCHANGE
  265. _fnpPropChg[14] = &CTxtEdit::OnTxBackStyleChange; // TXTBIT_BACKSTYLECHANGE
  266. _fnpPropChg[15] = &CTxtEdit::OnMaxLengthChange; // TXTBIT_MAXLENGTHCHANGE
  267. _fnpPropChg[16] = &CTxtEdit::OnScrollChange; // TXTBIT_SCROLLBARCHANGE
  268. _fnpPropChg[17] = &CTxtEdit::OnCharFormatChange; // TXTBIT_CHARFORMATCHANGE
  269. _fnpPropChg[18] = &CTxtEdit::OnParaFormatChange; // TXTBIT_PARAFORMATCHANGE
  270. _fnpPropChg[19] = &CTxtEdit::NeedViewUpdate; // TXTBIT_EXTENTCHANGE
  271. _fnpPropChg[20] = &CTxtEdit::OnClientRectChange; // TXTBIT_CLIENTRECTCHANGE
  272. }
  273. // Set up default CCharFormat and CParaFormat
  274. if (TxGetDefaultCharFormat(&CF, dwMask) != NOERROR ||
  275. TxGetDefaultParaFormat(&PF) != NOERROR ||
  276. FAILED(GetCharFormatCache()->Cache(&CF, &iCF)) ||
  277. FAILED(GetParaFormatCache()->Cache(&PF, &iPF)))
  278. {
  279. return FALSE;
  280. }
  281. GetTabsCache()->Release(PF._iTabs);
  282. Set_iCF(iCF); // Save format indices
  283. Set_iPF(iPF);
  284. // Load mouse cursors (but only for first instance)
  285. if(!_hcurArrow)
  286. {
  287. _hcurArrow = LoadCursor(0, IDC_ARROW);
  288. if(!_hcurHand)
  289. {
  290. if (W32->_dwMajorVersion < 5)
  291. _hcurHand = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_HAND));
  292. else
  293. _hcurHand = LoadCursor(0, IDC_HAND);
  294. }
  295. if(!_hcurIBeam) // Load cursor
  296. _hcurIBeam = LoadCursor(0, IDC_IBEAM);
  297. if(!_hcurItalic)
  298. _hcurItalic = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_ITALIC));
  299. if(!_hcurSelBar)
  300. _hcurSelBar = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_SELBAR));
  301. if(!_hcurVItalic)
  302. _hcurVItalic = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_VITALIC));
  303. if(!_hcurVIBeam)
  304. _hcurVIBeam = LoadCursor(hinstRE, MAKEINTRESOURCE(CUR_VIBEAM));
  305. }
  306. #ifdef DEBUG
  307. // The host is going to do some checking on richtext vs. plain text.
  308. _fRich = TRUE;
  309. #endif // DEBUG
  310. if(_phost->TxGetPropertyBits (TXTBITS | // Get host state flags
  311. TXTBIT_MULTILINE | TXTBIT_SHOWACCELERATOR, // that we cache or need
  312. &dwBits) != NOERROR) // for display setup
  313. {
  314. return FALSE;
  315. } // Cache bits defined by
  316. _dwFlags = dwBits & TXTBITS; // TXTBITS mask
  317. if ((dwBits & TXTBIT_SHOWACCELERATOR) && // They want accelerator,
  318. FAILED(UpdateAccelerator())) // so let's get it
  319. {
  320. return FALSE;
  321. }
  322. _fTransparent = TxGetBackStyle() == TXTBACK_TRANSPARENT;
  323. if(dwBits & TXTBIT_MULTILINE) // Create and initialize
  324. _pdp = new CDisplayML(this); // display
  325. else
  326. _pdp = new CDisplaySL(this);
  327. Assert(_pdp);
  328. if(!_pdp || !_pdp->Init())
  329. return FALSE;
  330. _fUseUndo = TRUE;
  331. _fAutoFont = TRUE;
  332. _fDualFont = TRUE;
  333. _f10DeferChangeNotify = 0;
  334. // Set whether we are in our host or not
  335. ITextHost2 *phost2;
  336. if(_phost->QueryInterface(IID_ITextHost2, (void **)&phost2) == NOERROR)
  337. {
  338. // We assume that ITextHost2 means this is our host
  339. phost2->Release();
  340. _fHost2 = TRUE;
  341. }
  342. else // Get maximum from our host
  343. _phost->TxGetMaxLength(&_cchTextMost);
  344. // Add EOP iff Rich Text
  345. if(IsRich())
  346. {
  347. // We should _not_ be in 10 compatibility mode yet.
  348. // If we transition into 1.0 mode, we'll add a CRLF
  349. // at the end of the document.
  350. SetRichDocEndEOP(0);
  351. }
  352. #ifndef NOLINESERVICES
  353. // Allow for win.ini control over use of line services
  354. if (W32->fUseLs())
  355. {
  356. OnSetTypographyOptions(TO_ADVANCEDTYPOGRAPHY, TO_ADVANCEDTYPOGRAPHY);
  357. }
  358. #ifndef NOCOMPLEXSCRIPTS
  359. if (W32->GetDigitSubstitutionMode() != DIGITS_NOTIMPL)
  360. OrCharFlags(FDIGITSHAPE); // digit substitution presents
  361. // Initialize the BiDi property
  362. // It is set to true if OS is BiDi (the system default LCID is a BiDi language)
  363. // or if the current keyboard code page is a BiDi code page
  364. // or if system.ini says we should do it.
  365. if (W32->OnBiDiOS() ||
  366. IsBiDiCharRep(GetKeyboardCharRep(0xFFFFFFFF)) ||
  367. W32->fUseBiDi())
  368. {
  369. OrCharFlags(FRTL);
  370. }
  371. _fAutoKeyboard = IsBiDi() && IsBiDiKbdInstalled();
  372. #endif // NOCOMPLEXSCRIPTS
  373. #endif
  374. return TRUE;
  375. }
  376. ///////////////////////////// CTxtEdit IUnknown ////////////////////////////////
  377. /*
  378. * CTxtEdit::QueryInterface (riid, ppv)
  379. *
  380. * @mfunc
  381. * IUnknown method
  382. *
  383. * @rdesc
  384. * HRESULT = (if success) ? NOERROR : E_NOINTERFACE
  385. *
  386. * @devnote
  387. * This interface is aggregated. See textserv.cpp for discussion.
  388. */
  389. HRESULT CTxtEdit::QueryInterface(
  390. REFIID riid,
  391. void **ppv)
  392. {
  393. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::QueryInterface");
  394. return _punk->QueryInterface(riid, ppv);
  395. }
  396. /*
  397. * CTxtEdit::AddRef()
  398. *
  399. * @mfunc
  400. * IUnknown method
  401. *
  402. * @rdesc
  403. * ULONG - incremented reference count
  404. */
  405. ULONG CTxtEdit::AddRef(void)
  406. {
  407. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::AddRef");
  408. return _punk->AddRef();
  409. }
  410. /*
  411. * CTxtEdit::Release()
  412. *
  413. * @mfunc
  414. * IUnknown method
  415. *
  416. * @rdesc
  417. * ULONG - decremented reference count
  418. */
  419. ULONG CTxtEdit::Release(void)
  420. {
  421. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::Release");
  422. return _punk->Release();
  423. }
  424. ////////////////////////// Undo Management //////////////////////////////
  425. /*
  426. * CTxtEdit::CreateUndoMgr (cUndoLim, flags)
  427. *
  428. * @mfunc
  429. * Creates an undo stack
  430. *
  431. * @rdesc
  432. * Ptr to new IUndoMgr
  433. */
  434. IUndoMgr *CTxtEdit::CreateUndoMgr(
  435. LONG cUndoLim, //@parm Size limit
  436. USFlags flags)
  437. {
  438. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CreateUndoMgr");
  439. if(!_fUseUndo)
  440. return NULL;
  441. IUndoMgr *pmgr = new CUndoStack(this, cUndoLim, flags);
  442. if(!pmgr)
  443. return NULL;
  444. if(!pmgr->GetUndoLimit())
  445. {
  446. // The undo stack failed to initialize properly (probably
  447. // lack of memory). Trash it and return NULL.
  448. pmgr->Destroy();
  449. return NULL;
  450. }
  451. // We may be asked to create a new undo/redo manager
  452. // before we are completely done with initialization.
  453. // We need to clean up memory we have already allocated.
  454. if(flags & US_REDO)
  455. {
  456. if(_predo)
  457. _predo->Destroy();
  458. _predo = pmgr;
  459. }
  460. else
  461. {
  462. if(_pundo)
  463. _pundo->Destroy();
  464. _pundo = pmgr;
  465. }
  466. return pmgr;
  467. }
  468. /*
  469. * CTxtEdit::HandleUndoLimit (cUndoLim)
  470. *
  471. * @mfunc
  472. * Handles the EM_SETUNDOLIMIT message
  473. *
  474. * @rdesc
  475. * Actual limit to which things were set.
  476. */
  477. LRESULT CTxtEdit::HandleSetUndoLimit(
  478. LONG cUndoLim) //@parm Requested limit size
  479. {
  480. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::HandleSetUndoLimit");
  481. if (cUndoLim == tomSuspend || // This option really just
  482. cUndoLim == tomResume) // suspends undo, i.e.,
  483. { // doesn't discard existing
  484. _fUseUndo = (cUndoLim == tomResume);// antievents
  485. return _pundo ? _pundo->GetUndoLimit() : 0;
  486. }
  487. if(cUndoLim < 0)
  488. cUndoLim = DEFAULT_UNDO_SIZE;
  489. if(!cUndoLim)
  490. {
  491. _fUseUndo = FALSE;
  492. if(_pundo)
  493. {
  494. _pundo->Destroy();
  495. _pundo = NULL;
  496. }
  497. if(_predo)
  498. {
  499. _predo->Destroy();
  500. _predo = NULL;
  501. }
  502. }
  503. else if(!_pundo)
  504. {
  505. _fUseUndo = TRUE;
  506. // Don't worry about return value; if it's NULL, we're
  507. // in the same boat as if the API wasn't called (so later
  508. // on, we might try to allocate the default).
  509. CreateUndoMgr(cUndoLim, US_UNDO);
  510. }
  511. else
  512. {
  513. cUndoLim = _pundo->SetUndoLimit(cUndoLim);
  514. // Setting the undo limit on the undo stack will return to
  515. // us the actual amount set. Try to set the redo stack to
  516. // the same size. If it can't go that big, too bad.
  517. if(_predo)
  518. _predo->SetUndoLimit(cUndoLim);
  519. }
  520. return cUndoLim;
  521. }
  522. /*
  523. * CTxtEdit::HandleSetTextMode(mode)
  524. *
  525. * @mfunc handles setting the text mode
  526. *
  527. * @rdesc LRESULT; 0 (NOERROR) on success, OLE failure code on failure.
  528. *
  529. * @devnote the text mode does not have to be fully specified; it
  530. * is sufficient to merely specify the specific desired behavior.
  531. *
  532. * Note that the edit control must be completely empty for this
  533. * routine to work.
  534. */
  535. LRESULT CTxtEdit::HandleSetTextMode(
  536. DWORD mode) //@parm the desired mode
  537. {
  538. LRESULT lres = 0;
  539. // First off, we must be completely empty
  540. if (GetAdjustedTextLength() ||
  541. _pundo && _pundo->CanUndo() ||
  542. _predo && _predo->CanUndo())
  543. {
  544. return E_UNEXPECTED;
  545. }
  546. // These bits are considered one at a time; thus the absence of
  547. // any bits does _NOT_ imply any change in behavior.
  548. // TM_RICHTEXT && TM_PLAINTEXT are mutually exclusive; they cannot
  549. // be both set. Same goes for TM_SINGLELEVELUNDO / TM_MULTILEVELUNDO
  550. // and TM_SINGLECODEPAGE / TM_MULTICODEPAGE
  551. if((mode & (TM_RICHTEXT | TM_PLAINTEXT)) == (TM_RICHTEXT | TM_PLAINTEXT) ||
  552. (mode & (TM_SINGLELEVELUNDO | TM_MULTILEVELUNDO)) ==
  553. (TM_SINGLELEVELUNDO | TM_MULTILEVELUNDO) ||
  554. (mode & (TM_SINGLECODEPAGE | TM_MULTICODEPAGE)) ==
  555. (TM_SINGLECODEPAGE | TM_MULTICODEPAGE))
  556. {
  557. lres = E_INVALIDARG;
  558. }
  559. else if((mode & TM_PLAINTEXT) && IsRich())
  560. lres = OnRichEditChange(FALSE);
  561. else if((mode & TM_RICHTEXT) && !IsRich())
  562. lres = OnRichEditChange(TRUE);
  563. if(!lres)
  564. {
  565. if(mode & TM_SINGLELEVELUNDO)
  566. {
  567. if(!_pundo)
  568. CreateUndoMgr(1, US_UNDO);
  569. if(_pundo)
  570. {
  571. // We can 'Enable' single level mode as many times
  572. // as we want, so no need to check for it before hand.
  573. lres = ((CUndoStack *)_pundo)->EnableSingleLevelMode();
  574. }
  575. else
  576. lres = E_OUTOFMEMORY;
  577. }
  578. else if(mode & TM_MULTILEVELUNDO)
  579. {
  580. // If there's no undo stack, no need to do anything,
  581. // we're already in multi-level mode
  582. if(_pundo && ((CUndoStack *)_pundo)->GetSingleLevelMode())
  583. ((CUndoStack *)_pundo)->DisableSingleLevelMode();
  584. }
  585. if(mode & TM_SINGLECODEPAGE)
  586. _fSingleCodePage = TRUE;
  587. else if(mode & TM_MULTICODEPAGE)
  588. _fSingleCodePage = FALSE;
  589. }
  590. // We don't want this marked modified after this operation to make us
  591. // work better in dialog boxes.
  592. _fModified = FALSE;
  593. return lres;
  594. }
  595. /*
  596. * CTxtEdit::HandleSetTextFlow(mode)
  597. *
  598. * @mfunc handles setting the text flow
  599. *
  600. * @rdesc LRESULT; 0 (NOERROR) on success, 1 (S_FALSE) for invalide mode
  601. *
  602. */
  603. LRESULT CTxtEdit::HandleSetTextFlow(
  604. DWORD mode) //@parm the desired mode
  605. {
  606. TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::HandleSetTextFlow");
  607. if (!IN_RANGE(0, mode, 3) || !_pdp)
  608. return S_FALSE;
  609. if (mode == _pdp->GetTflow()) // No change
  610. return NOERROR;
  611. // We pretend like something actually happened.
  612. GetCallMgr()->SetChangeEvent(CN_GENERIC);
  613. _pdp->SetTflow(mode);
  614. TxShowScrollBar(SB_HORZ, _pdp->IsUScrollEnabled());
  615. TxShowScrollBar(SB_VERT, _pdp->IsVScrollEnabled());
  616. NeedViewUpdate(TRUE);
  617. return NOERROR;
  618. }
  619. extern ICustomTextOut *g_pcto;
  620. /*
  621. * CTxtEdit::GetCcs()
  622. *
  623. * @mfunc
  624. * Fetches a CCcs for a specific CCharFormat
  625. *
  626. * @rdesc
  627. * Ptr to CCcs
  628. */
  629. CCcs* CTxtEdit::GetCcs(
  630. const CCharFormat *const pCF,
  631. const LONG dvpInch,
  632. DWORD dwFlags,
  633. HDC hdc)
  634. {
  635. //Note, don't do ClearType for metafiles or printing.
  636. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetCcs");
  637. CCharFormat CF = *pCF;
  638. if (g_pcto && FUseCustomTextOut())
  639. CF._dwEffects |= CFE_CUSTOMTEXTOUT;
  640. #ifndef NODRAFTMODE
  641. // Use draft mode font only for displays
  642. if (_fDraftMode && (!hdc || GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY))
  643. {
  644. SHORT iFont;
  645. SHORT yHeight;
  646. QWORD qwFontSig;
  647. COLORREF crColor;
  648. if (W32->GetDraftModeFontInfo(iFont, yHeight, qwFontSig, crColor))
  649. {
  650. // Only hammer the name if the charset is OK
  651. if (FontSigFromCharRep(CF._iCharRep) & qwFontSig)
  652. CF._iFont = iFont;
  653. // Hammer the size always
  654. CF._yHeight = yHeight;
  655. }
  656. }
  657. #endif
  658. if (dwFlags == -1)
  659. dwFlags = _pdp->GetTflow();
  660. if (_fUseAtFont)
  661. dwFlags |= FGCCSUSEATFONT;
  662. return fc().GetCcs(&CF, dvpInch, dwFlags, hdc);
  663. }
  664. CHyphCache *g_phc;
  665. /*
  666. * CTxtEdit::GetHyphCache()
  667. *
  668. * @mfunc
  669. * returns a pointer to the CHyphCache class (creating it if necessary)
  670. *
  671. * @rdesc
  672. * Ptr to CHyphCache class
  673. */
  674. CHyphCache* CTxtEdit::GetHyphCache(void)
  675. {
  676. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetHyphCache");
  677. if (!g_phc)
  678. g_phc = new CHyphCache();
  679. return g_phc;
  680. }
  681. ////////////////////////// Uniscribe Interface //////////////////////////////
  682. #ifndef NOCOMPLEXSCRIPTS
  683. /*
  684. * GetUniscribe()
  685. *
  686. * @mfunc
  687. * returns a pointer to the Uniscribe interface object
  688. *
  689. * @rdesc
  690. * Ptr to Uniscribe interface
  691. */
  692. extern BOOL g_fNoUniscribe;
  693. CUniscribe* GetUniscribe()
  694. {
  695. if (g_pusp)
  696. return g_pusp;
  697. if (g_fNoUniscribe)
  698. return NULL;
  699. //Attempt to create the Uniscribe object, but make sure the
  700. //OS is valid and that we can load the uniscribe DLL.
  701. int cScripts;
  702. //Find out if OS is valid, or if delay-load fails
  703. if (!IsSupportedOS() || FAILED(ScriptGetProperties(NULL, &cScripts)))
  704. {
  705. g_fNoUniscribe = TRUE;
  706. return NULL;
  707. }
  708. if (!g_pusp)
  709. g_pusp = new CUniscribe();
  710. AssertSz(g_pusp, "GetUniscribe(): Create Uniscribe object failed");
  711. return g_pusp;
  712. }
  713. #endif // NOCOMPLEXSCRIPTS
  714. ////////////////////////// Notification Manager //////////////////////////////
  715. /*
  716. * CTxtEdit::GetNotifyMgr()
  717. *
  718. * @mfunc
  719. * returns a pointer to the notification manager (creating it if necessary)
  720. *
  721. * @rdesc
  722. * Ptr to notification manager
  723. */
  724. CNotifyMgr *CTxtEdit::GetNotifyMgr()
  725. {
  726. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetNotifyMgr");
  727. return &_nm;
  728. }
  729. ////////////////////////// Object Manager ///////////////////////////////////
  730. /*
  731. * CTxtEdit::GetObjectMgr()
  732. *
  733. * @mfunc
  734. * returns a pointer to the object manager (creating if necessary)
  735. *
  736. * @rdesc
  737. * pointer to the object manager
  738. */
  739. CObjectMgr *CTxtEdit::GetObjectMgr()
  740. {
  741. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetObjectMgr");
  742. if(!_pobjmgr)
  743. _pobjmgr = new CObjectMgr();
  744. return _pobjmgr;
  745. }
  746. ////////////////////////////// Properties - Selection ////////////////////////////////
  747. LONG CTxtEdit::GetSelMin() const
  748. {
  749. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetSelMin");
  750. return _psel ? _psel->GetCpMin() : 0;
  751. }
  752. LONG CTxtEdit::GetSelMost() const
  753. {
  754. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetSelMost");
  755. return _psel ? _psel->GetCpMost() : 0;
  756. }
  757. ////////////////////////////// Properties - Text //////////////////////////////////////
  758. LONG CTxtEdit::GetTextRange(
  759. LONG cpFirst,
  760. LONG cch,
  761. WCHAR * pch)
  762. {
  763. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetTextRange");
  764. #ifdef DEBUG
  765. const LONG cchAsk = cch;
  766. #endif
  767. CTxtPtr tp(this, cpFirst);
  768. LONG cchAdj = GetAdjustedTextLength();
  769. if(--cch < 0 || cpFirst > cchAdj)
  770. return 0;
  771. cch = min(cch, cchAdj - cpFirst);
  772. if(cch > 0)
  773. {
  774. cch = tp.GetPlainText(cch, pch, cpFirst + cch, FALSE, FALSE);
  775. Assert(cch >= 0);
  776. }
  777. pch[cch] = TEXT('\0');
  778. #ifdef DEBUG
  779. if(cch != cchAsk - 1)
  780. Tracef(TRCSEVINFO, "CTxtEdit::GetTextRange: only got %ld out of %ld", cch, cchAsk - 1);
  781. #endif
  782. return cch;
  783. }
  784. /*
  785. * CTxtEdit::GetTextEx (pgt, pch)
  786. *
  787. * @mfunc
  788. * Grabs text according to various params
  789. *
  790. * @rdesc
  791. * Count of bytes gotten
  792. */
  793. LONG CTxtEdit::GetTextEx(
  794. GETTEXTEX *pgt, //@parm Info on what to get
  795. WCHAR * pch) //@parm Where to put the text
  796. {
  797. LONG cb;
  798. LONG cch;
  799. LONG cchGet = GetAdjustedTextLength();
  800. LONG cpMin = 0;
  801. LONG cpMost = tomForward;
  802. WCHAR * pchUse = pch;
  803. CTempWcharBuf twcb;
  804. if(pgt->flags & GT_SELECTION) // Get selected text
  805. {
  806. cch = GetSel()->GetRange(cpMin, cpMost);
  807. cchGet = min(cch, cchGet - cpMin); // Don't include final EOP
  808. }
  809. if(pgt->codepage == (unsigned)-1) // Use default codepage
  810. pgt->codepage = GetDefaultCodePage(EM_GETTEXTEX);
  811. if(pgt->cb == (unsigned)-1) // Client says its buffer is big enuf
  812. {
  813. pgt->cb = cchGet + 1;
  814. if(W32->IsFECodePage(pgt->codepage) || pgt->codepage == 1200)
  815. pgt->cb += cchGet;
  816. else if(pgt->codepage == CP_UTF8 && (_qwCharFlags & ~FASCII))
  817. pgt->cb *= (_qwCharFlags & FABOVEX7FF) ? 3 : 2;
  818. }
  819. // Allocate a big buffer; make sure that we have
  820. // enough room for lots of CRLFs if necessary
  821. if(pgt->flags & GT_USECRLF)
  822. cchGet *= 2;
  823. if(pgt->codepage != 1200)
  824. {
  825. // If UNICODE, copy straight to client's buffer;
  826. // else, copy to temp buffer and translate cases first
  827. pchUse = twcb.GetBuf(cchGet + 1);
  828. if (pch)
  829. *((char *)pch) = '\0'; // In case something fails
  830. }
  831. else // Be sure to leave room for NULL terminator
  832. cchGet = min(UINT(pgt->cb/2 - 1), (UINT)cchGet);
  833. // Now grab the text
  834. if(pgt->flags & GT_NOHIDDENTEXT)
  835. {
  836. CRchTxtPtr rtp(this, cpMin);
  837. cch = rtp.GetPlainText(cchGet, pchUse, cpMost, FALSE, pgt->flags & GT_USECRLF);
  838. }
  839. else
  840. {
  841. CTxtPtr tp(this, cpMin);
  842. if(pgt->flags & GT_RAWTEXT)
  843. cch = tp.GetText(cchGet, pchUse);
  844. else
  845. cch = tp.GetPlainText(cchGet, pchUse, cpMost, FALSE, pgt->flags & GT_USECRLF);
  846. }
  847. pchUse[cch] = L'\0';
  848. // If we're just doing UNICODE, return number of chars written
  849. if(pgt->codepage == 1200)
  850. return cch;
  851. // Oops, gotta translate to ANSI
  852. cb = WideCharToMultiByte(pgt->codepage, 0, pchUse, cch + 1, (char *)pch,
  853. pgt->cb, pgt->lpDefaultChar, pgt->lpUsedDefChar);
  854. // Don't count NULL terminator for compatibility with WM_GETTEXT
  855. return cb ? cb - 1 : 0;
  856. }
  857. /*
  858. * CTxtEdit::GetTextLengthEx (pgtl)
  859. *
  860. * @mfunc
  861. * Calculates text length in various ways.
  862. *
  863. * @rdesc
  864. * Text length calculated in various ways
  865. *
  866. * @comm
  867. * This function returns an API cp that may differ from the
  868. * corresponding internal Unicode cp.
  869. */
  870. LONG CTxtEdit::GetTextLengthEx(
  871. GETTEXTLENGTHEX *pgtl) //@parm Info describing how to calculate length
  872. {
  873. LONG cchUnicode = GetAdjustedTextLength();
  874. LONG cEOP = 0;
  875. DWORD dwFlags = pgtl->flags;
  876. GETTEXTEX gt;
  877. if(pgtl->codepage == (unsigned)-1)
  878. pgtl->codepage = GetDefaultCodePage(EM_GETTEXTLENGTHEX);
  879. // Make sure the flags are defined appropriately
  880. if ((dwFlags & GTL_CLOSE) && (dwFlags & GTL_PRECISE) ||
  881. (dwFlags & GTL_NUMCHARS) && (dwFlags & GTL_NUMBYTES))
  882. {
  883. TRACEWARNSZ("Invalid flags for EM_GETTEXTLENGTHEX");
  884. return E_INVALIDARG;
  885. }
  886. // Note in the following if statement, the second part of the
  887. // and clause will always be TRUE. At some point in the future
  888. // fUseCRLF and Get10Mode may become independent, in which case
  889. // the code below will automatically work without change.
  890. // NEW with 4.0: 1.0 mode gets text as is, so don't add count for CRs.
  891. // (RichEdit 1.0 only inserts Enters as CRLFs; it doesn't "cleanse"
  892. // other text insertion strings)
  893. if((dwFlags & GTL_USECRLF) && !fUseCRLF() && !Get10Mode())
  894. {
  895. // Important facts for 1.0 mode (REMARK: this is out of date):
  896. //
  897. // (1) 1.0 mode implies that the text is stored with fUseCRLF true.
  898. // fUseCRLF means that the EOP mark can either be a CR or a
  899. // CRLF - see CTxtRange::CleanseAndReplaceRange for details.
  900. //
  901. // (2) 1.0 mode has an invariant that the count of text returned
  902. // by this call should be enough to hold all the text returned by
  903. // WM_GETTEXT.
  904. //
  905. // (3) The WM_GETEXT call for 1.0 mode will return a buffer in
  906. // which all EOPs that consist of a CR are replaced by CRLF.
  907. //
  908. // Therefore, for 1.0 mode, we must count all EOPs that consist
  909. // of only a CR and add addition return character to count the
  910. // LF that will be added into any WM_GETEXT buffer.
  911. // For 2.0 mode, the code is much easier, just count up all
  912. // CRs and bump count of each one by 1.
  913. CTxtPtr tp(this, 0);
  914. LONG Results;
  915. while(tp.FindEOP(tomForward, &Results))
  916. {
  917. // If EOP consists of 1 char, add 1 since is returned by a CRLF.
  918. // If it consists of 2 chars, add 0, since it's a CRLF and is
  919. // returned as such.
  920. if(tp.GetCp() > cchUnicode) // Don't add correction for
  921. break; // final CR (if any)
  922. if (!(Results & FEOP_CELL) && // CELL gets xlated into TAB,
  923. tp.GetPrevChar() != FF) // and FF into FF,
  924. { // i.e., single chars
  925. Results &= 3; // Get advance cch
  926. if(Results)
  927. cEOP += 2 - Results; // Add in xtra if lone CR or LF
  928. }
  929. AssertSz(IN_RANGE(1, Results & 3, 2) || !Results && tp.GetCp() == cchUnicode,
  930. "CTxtEdit::GetTextLengthEx: CRCRLF found in backing store");
  931. }
  932. cchUnicode += cEOP;
  933. }
  934. // If we're just looking for the number of characters or if it's an
  935. // 8-bit codepage in RE 1.0 mode, we've already got the count.
  936. if ((dwFlags & GTL_NUMCHARS) || !dwFlags ||
  937. Get10Mode() && Is8BitCodePage(pgtl->codepage))
  938. {
  939. return cchUnicode;
  940. }
  941. // Hmm, they're looking for number of bytes, but don't care about
  942. // precision, just multiply by two. If neither PRECISE or CLOSE is
  943. // specified, default to CLOSE. Note if the codepage is UNICODE and
  944. // asking for number of bytes, we also just multiply by 2.
  945. if((dwFlags & GTL_CLOSE) || !(dwFlags & GTL_PRECISE) ||
  946. pgtl->codepage == 1200)
  947. {
  948. return cchUnicode *2;
  949. }
  950. // In order to get a precise answer, we need to convert (which is slow!).
  951. gt.cb = 0;
  952. gt.flags = (pgtl->flags & GT_USECRLF);
  953. gt.codepage = pgtl->codepage;
  954. gt.lpDefaultChar = NULL;
  955. gt.lpUsedDefChar = NULL;
  956. return GetTextEx(&gt, NULL);
  957. }
  958. /*
  959. * CTxtEdit::GetDefaultCodePage (msg)
  960. *
  961. * @mfunc
  962. * Return codepage to use for converting the text in RichEdit20A text
  963. * messages.
  964. *
  965. * @rdesc
  966. * Codepage to use for converting the text in RichEdit20A text messages.
  967. */
  968. LONG CTxtEdit::GetDefaultCodePage(
  969. UINT msg)
  970. {
  971. LONG CodePage = GetACP();
  972. // FUTURE: For backward compatibility in Office97, We always use ACP for all these
  973. // languages. Need review in the future when the world all moves to Unicode.
  974. if (W32->IsBiDiCodePage(CodePage) || CodePage == CP_THAI || CodePage == CP_VIETNAMESE ||
  975. W32->IsFECodePage(CodePage) || _fSingleCodePage || msg == EM_GETCHARFORMAT ||
  976. msg == EM_SETCHARFORMAT)
  977. {
  978. return CodePage;
  979. }
  980. if(Get10Mode())
  981. return CodePageFromCharRep(GetCharFormat(-1)->_iCharRep);
  982. return CodePageFromCharRep(GetKeyboardCharRep());
  983. }
  984. ////////////////////////////// Properties - Formats //////////////////////////////////
  985. /*
  986. * CTxtEdit::HandleStyle (pCFTarget, pCF, dwMask, dwMask2)
  987. *
  988. * @mfunc
  989. * If pCF specifies a style choice, initialize pCFTarget with the
  990. * appropriate style, apply pCF, and return NOERROR. Else return
  991. * S_FALSE or an error
  992. *
  993. * @rdesc
  994. * HRESULT = (pCF specifies a style choice) ? NOERROR : S_FALSE or error code
  995. */
  996. HRESULT CTxtEdit::HandleStyle(
  997. CCharFormat *pCFTarget, //@parm Target CF to receive CF style content
  998. const CCharFormat *pCF, //@parm Source CF that may specify a style
  999. DWORD dwMask, //@parm CHARFORMAT2 mask
  1000. DWORD dwMask2) //@parm Second mask
  1001. {
  1002. if(pCF->fSetStyle(dwMask, dwMask2))
  1003. {
  1004. // FUTURE: generalize to use client style if specified
  1005. *pCFTarget = *GetCharFormat(-1);
  1006. pCFTarget->ApplyDefaultStyle(pCF->_sStyle);
  1007. return pCFTarget->Apply(pCF, dwMask, dwMask2);
  1008. }
  1009. return S_FALSE;
  1010. }
  1011. /*
  1012. * CTxtEdit::HandleStyle (pPFTarget, pPF)
  1013. *
  1014. * @mfunc
  1015. * If pPF specifies a style choice, initialize pPFTarget with the
  1016. * appropriate style, apply pPF, and return NOERROR. Else return
  1017. * S_FALSE or an error
  1018. *
  1019. * @rdesc
  1020. * HRESULT = (pPF specifies a style choice) ? NOERROR : S_FALSE or error code
  1021. */
  1022. HRESULT CTxtEdit::HandleStyle(
  1023. CParaFormat *pPFTarget, //@parm Target PF to receive PF style content
  1024. const CParaFormat *pPF, //@parm Source PF that may specify a style
  1025. DWORD dwMask, //@parm Mask to use in setting CParaFormat
  1026. DWORD dwMask2) //@parm Mask for internal flags
  1027. {
  1028. if(pPF->fSetStyle(dwMask, dwMask2))
  1029. {
  1030. // FUTURE: generalize to use client style if specified
  1031. *pPFTarget = *GetParaFormat(-1);
  1032. pPFTarget->ApplyDefaultStyle(pPF->_sStyle);
  1033. return pPFTarget->Apply(pPF, dwMask, dwMask2);
  1034. }
  1035. return S_FALSE;
  1036. }
  1037. //////////////////////////// Mouse Commands /////////////////////////////////
  1038. HRESULT CTxtEdit::OnTxLButtonDblClk(
  1039. INT x, //@parm Mouse x coordinate
  1040. INT y, //@parm Mouse y coordinate
  1041. DWORD dwFlags) //@parm Mouse message wparam
  1042. {
  1043. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxLButtonDblClk");
  1044. BOOL fEnterParaSelMode = FALSE;
  1045. HITTEST Hit;
  1046. CTxtSelection * psel = GetSel();
  1047. const POINT ptxy = {x, y};
  1048. POINTUV pt;
  1049. AssertSz(psel, "CTxtEdit::OnTxLButtonDblClk() - No selection object !");
  1050. if (StopMagellanScroll())
  1051. return S_OK;
  1052. _dwTickDblClick = GetTickCount();
  1053. _ptDblClick.x = x;
  1054. _ptDblClick.y = y;
  1055. TxUpdateWindow(); // Repaint window to show any exposed portions
  1056. if(!_fFocus)
  1057. {
  1058. TxSetFocus(); // Create and display caret
  1059. return S_OK;
  1060. }
  1061. _pdp->PointuvFromPoint(pt, ptxy);
  1062. // Find out what the cursor is pointing at
  1063. _pdp->CpFromPoint(pt, NULL, NULL, NULL, FALSE, &Hit);
  1064. if(Hit == HT_Nothing)
  1065. return S_OK;
  1066. if(Hit == HT_OutlineSymbol)
  1067. {
  1068. CTxtRange rg(*psel);
  1069. rg.ExpandOutline(0, FALSE);
  1070. return S_OK;
  1071. }
  1072. if(Hit == HT_LeftOfText)
  1073. fEnterParaSelMode = TRUE;
  1074. _fWantDrag = FALSE; // just to be safe
  1075. // If we are over a link, let the client have a chance to process
  1076. // the message
  1077. if(Hit == HT_Link && HandleLinkNotification(WM_LBUTTONDBLCLK, (WPARAM)dwFlags, MAKELPARAM(x, y)))
  1078. return S_OK;
  1079. if(dwFlags & MK_CONTROL)
  1080. return S_OK;
  1081. // Mark mouse down
  1082. _fMouseDown = TRUE;
  1083. if(_pobjmgr && _pobjmgr->HandleDoubleClick(this, pt, dwFlags))
  1084. {
  1085. // The object subsystem handled everything
  1086. _fMouseDown = FALSE;
  1087. return S_OK;
  1088. }
  1089. // Update the selection
  1090. if(fEnterParaSelMode)
  1091. psel->SelectUnit(pt, tomParagraph);
  1092. else
  1093. psel->SelectWord(pt);
  1094. return S_OK;
  1095. }
  1096. HRESULT CTxtEdit::OnTxLButtonDown(
  1097. INT x, //@parm Mouse x coordinate
  1098. INT y, //@parm Mouse y coordinate
  1099. DWORD dwFlags) //@parm Mouse message wparam
  1100. {
  1101. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxLButtonDown");
  1102. BOOL fEnterLineSelMode = FALSE;
  1103. BOOL fShift = dwFlags & MK_SHIFT;
  1104. HITTEST Hit;
  1105. POINTUV pt;
  1106. const POINT ptxy = {x, y};
  1107. COleObject *pobj;
  1108. BOOL fMustThaw = FALSE;
  1109. const BOOL fTripleClick = GetTickCount() < _dwTickDblClick + W32->GetDCT() &&
  1110. abs(x - _ptDblClick.x) <= W32->GetCxDoubleClk() &&
  1111. abs(y - _ptDblClick.y) <= W32->GetCyDoubleClk();
  1112. if (StopMagellanScroll())
  1113. return S_OK;
  1114. _pdp->PointuvFromPoint(pt, ptxy);
  1115. // If click isn't inside view, just activate, don't select
  1116. if(!_fFocus) // Sets focus if not already
  1117. {
  1118. // We may be removing an existing selection, so freeze
  1119. // display to avoid flicker
  1120. _pdp->Freeze();
  1121. fMustThaw = TRUE;
  1122. TxSetFocus(); // creates and displays caret
  1123. }
  1124. // Grab selection object
  1125. CTxtSelection * const psel = GetSel();
  1126. AssertSz(psel,"CTxtEdit::OnTxLButtonDown - No selection object !");
  1127. // Find out what cursor is pointing at
  1128. _pdp->CpFromPoint(pt, NULL, NULL, NULL, FALSE, &Hit);
  1129. if(Hit == HT_LeftOfText)
  1130. {
  1131. // Shift click in sel bar treated as normal click
  1132. if(!fShift)
  1133. {
  1134. // Control selbar click and triple selbar click
  1135. // are select all
  1136. if((dwFlags & MK_CONTROL) || fTripleClick)
  1137. {
  1138. psel->SelectAll();
  1139. goto cancel_modes;
  1140. }
  1141. fEnterLineSelMode = TRUE;
  1142. if(!GetAdjustedTextLength() && !_pdp->IsMultiLine())
  1143. {
  1144. const CParaFormat *pPF = psel->GetPF();
  1145. // Can't see selected para mark when flushed right, so
  1146. // leave selection as an insertion point
  1147. if(pPF->_bAlignment == PFA_RIGHT && !pPF->IsRtlPara())
  1148. fEnterLineSelMode = FALSE;
  1149. }
  1150. }
  1151. }
  1152. else if(Hit == HT_Nothing)
  1153. goto cancel_modes;
  1154. else if(!fShift)
  1155. psel->CancelModes();
  1156. // Let client have a chance to handle this message if we are over a link
  1157. if(Hit == HT_Link && HandleLinkNotification(WM_LBUTTONDOWN, (WPARAM)dwFlags,
  1158. MAKELPARAM(x, y)))
  1159. {
  1160. goto cancel_modes;
  1161. }
  1162. _fMouseDown = TRUE; // Flag mouse down
  1163. if(!fShift && _pobjmgr)
  1164. {
  1165. // Deactivate anybody active, etc.
  1166. ClickStatus status = _pobjmgr->HandleClick(this, pt);
  1167. if(status == CLICK_OBJSELECTED)
  1168. {
  1169. // The object subsystem will handle resizing.
  1170. // if not a resize we will signal start of drag
  1171. pobj = _pobjmgr->GetSingleSelect();
  1172. // Because HandleClick returned true, pobj better be non-null.
  1173. Assert(pobj);
  1174. if (!pobj->HandleResize(ptxy))
  1175. _fWantDrag = !_fDisableDrag;
  1176. goto cancel_modes;
  1177. }
  1178. else if(status == CLICK_OBJDEACTIVATED)
  1179. goto cancel_modes;
  1180. }
  1181. _fCapture = TRUE; // Capture the mouse
  1182. TxSetCapture(TRUE);
  1183. // Check for start of drag and drop
  1184. if(!fTripleClick && !fShift && psel->PointInSel(pt, NULL, Hit) && !_fDisableDrag)
  1185. {
  1186. // Assume we want a drag. If we don't CmdLeftUp() needs
  1187. // this to be set anyway to change the selection
  1188. _fWantDrag = TRUE;
  1189. goto cancel_modes;
  1190. }
  1191. if(fShift) // Extend selection from current
  1192. { // active end to click
  1193. psel->InitClickForAutWordSel(pt);
  1194. psel->ExtendSelection(pt);
  1195. }
  1196. else if(fEnterLineSelMode) // Line selection mode: select line
  1197. psel->SelectUnit(pt, tomLine);
  1198. else if(fTripleClick || Hit == HT_OutlineSymbol) // paragraph selection mode
  1199. psel->SelectUnit(pt, tomParagraph);
  1200. else
  1201. {
  1202. if (Get10Mode())
  1203. _f10DeferChangeNotify = 1;
  1204. psel->SetCaret(pt);
  1205. _mousePt = ptxy;
  1206. }
  1207. if(fMustThaw)
  1208. _pdp->Thaw();
  1209. return S_OK;
  1210. cancel_modes:
  1211. psel->CancelModes();
  1212. if(_fWantDrag)
  1213. {
  1214. TxSetTimer(RETID_DRAGDROP, W32->GetDragDelay());
  1215. _mousePt = ptxy;
  1216. _bMouseFlags = (BYTE)dwFlags;
  1217. _fDragged = FALSE;
  1218. }
  1219. if(fMustThaw)
  1220. _pdp->Thaw();
  1221. return S_OK;
  1222. }
  1223. HRESULT CTxtEdit::OnTxLButtonUp(
  1224. INT x, //@parm Mouse x coordinate
  1225. INT y, //@parm Mouse y coordinate
  1226. DWORD dwFlags, //@parm Mouse message wparam
  1227. int ffOptions) //@parm Mouse options, see _edit.h for details
  1228. {
  1229. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxLButtonUp");
  1230. CheckRemoveContinuousScroll();
  1231. // Remove capture before test for mouse down since we wait till
  1232. // we get the mouse button up message to release capture since Forms
  1233. // wants it that way.
  1234. if(_fCapture && (ffOptions & LB_RELEASECAPTURE))
  1235. {
  1236. TxSetCapture(FALSE);
  1237. _fCapture = FALSE;
  1238. }
  1239. // We were delaying selection change. So send it now...
  1240. if (DelayChangeNotification() && (ffOptions & LB_FLUSHNOTIFY))
  1241. {
  1242. AssertSz(Get10Mode(), "Flag should only be set in 10 mode");
  1243. _f10DeferChangeNotify = 0;
  1244. GetCallMgr()->SetSelectionChanged();
  1245. }
  1246. if(!_fMouseDown)
  1247. {
  1248. // We noticed the mouse was no longer down earlier so we don't
  1249. // need to do anything.
  1250. return S_OK;
  1251. }
  1252. const BOOL fSetSel = !!_fWantDrag;
  1253. const POINT ptxy = {x, y};
  1254. POINTUV pt;
  1255. _pdp->PointuvFromPoint(pt, ptxy);
  1256. // Cancel Auto Word Sel if on
  1257. CTxtSelection * const psel = GetSel();
  1258. AssertSz(psel,"CTxtEdit::OnLeftUp() - No selection object !");
  1259. psel->CancelModes(TRUE);
  1260. // Reset flags
  1261. _fMouseDown = FALSE;
  1262. _fWantDrag = FALSE;
  1263. _fDragged = FALSE;
  1264. TxKillTimer(RETID_DRAGDROP);
  1265. if(IsInOutlineView())
  1266. psel->Update(FALSE);
  1267. // Let the client handle this message if we are over a
  1268. // link area
  1269. if(HandleLinkNotification(WM_LBUTTONUP, (WPARAM)dwFlags,
  1270. MAKELPARAM(x, y)))
  1271. {
  1272. return NOERROR;
  1273. }
  1274. // If we were in drag & drop, put caret under mouse
  1275. if(fSetSel)
  1276. {
  1277. CObjectMgr* pobjmgr = GetObjectMgr();
  1278. // If we were on an object, don't deselect it by setting the caret
  1279. if(pobjmgr && !pobjmgr->GetSingleSelect())
  1280. {
  1281. psel->SetCaret(pt, TRUE);
  1282. if(!_fFocus)
  1283. TxSetFocus(); // create and display caret
  1284. }
  1285. }
  1286. return S_OK;
  1287. }
  1288. HRESULT CTxtEdit::OnTxRButtonUp(
  1289. INT x, //@parm Mouse x coordinate
  1290. INT y, //@parm Mouse y coordinate
  1291. DWORD dwFlags, //@parm Mouse message wparam
  1292. int ffOptions) //@parm option flag
  1293. {
  1294. const POINT ptxy = {x, y};
  1295. POINTUV pt;
  1296. CTxtSelection * psel;
  1297. SELCHANGE selchg;
  1298. HMENU hmenu = NULL;
  1299. IOleObject * poo = NULL;
  1300. COleObject * pobj = NULL;
  1301. IUnknown * pUnk = NULL;
  1302. IRichEditOleCallback * precall = NULL;
  1303. _pdp->PointuvFromPoint(pt, ptxy);
  1304. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxRButtonUp");
  1305. // Make sure we have the focus
  1306. if(!_fFocus)
  1307. TxSetFocus();
  1308. if(_fWantDrag)
  1309. {
  1310. _fDragged = FALSE;
  1311. _fWantDrag = FALSE;
  1312. TxKillTimer(RETID_DRAGDROP);
  1313. }
  1314. // Grab selection object
  1315. psel = GetSel();
  1316. psel->SetSelectionInfo(&selchg);
  1317. // We need a pointer to the first object, if any, in the selection.
  1318. if(_pobjmgr)
  1319. {
  1320. //If the point is in the selection we need to find out if there
  1321. //are any objects in the selection. If the point is not in a
  1322. //selection but it is on an object, we need to select the object.
  1323. if(psel->PointInSel(pt) || (ffOptions & RB_FORCEINSEL))
  1324. {
  1325. pobj = _pobjmgr->GetFirstObjectInRange(selchg.chrg.cpMin,
  1326. selchg.chrg.cpMost);
  1327. }
  1328. else
  1329. {
  1330. //Select the object
  1331. if(_pobjmgr->HandleClick(this, pt) == CLICK_OBJSELECTED)
  1332. {
  1333. pobj = _pobjmgr->GetSingleSelect();
  1334. // Because HandleClick returned true, pobj better be non-null.
  1335. Assert(pobj!=NULL);
  1336. //Refresh our information about the selection
  1337. psel = GetSel();
  1338. psel->SetSelectionInfo(&selchg);
  1339. }
  1340. }
  1341. precall = _pobjmgr->GetRECallback();
  1342. }
  1343. if(pobj)
  1344. pUnk = pobj->GetIUnknown();
  1345. if(pUnk)
  1346. pUnk->QueryInterface(IID_IOleObject, (void **)&poo);
  1347. if(precall)
  1348. precall->GetContextMenu(selchg.seltyp, poo, &selchg.chrg, &hmenu);
  1349. if(hmenu)
  1350. {
  1351. HWND hwnd, hwndParent;
  1352. POINT ptscr;
  1353. if(TxGetWindow(&hwnd) == NOERROR)
  1354. {
  1355. if(!(ffOptions & RB_NOSELCHECK) && !psel->PointInSel(pt) &&
  1356. !psel->GetCch() && !(ffOptions & RB_FORCEINSEL))
  1357. psel->SetCaret(pt);
  1358. ptscr.x = ptxy.x;
  1359. ptscr.y = ptxy.y;
  1360. ClientToScreen(hwnd, &ptscr);
  1361. hwndParent = GetParent(hwnd);
  1362. if(!hwndParent)
  1363. hwndParent = hwnd;
  1364. TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  1365. ptscr.x, ptscr.y, 0, hwndParent, NULL);
  1366. }
  1367. DestroyMenu(hmenu);
  1368. }
  1369. if(poo)
  1370. poo->Release();
  1371. return precall ? S_OK : S_FALSE;
  1372. }
  1373. HRESULT CTxtEdit::OnTxRButtonDown(
  1374. INT x, //@parm Mouse x coordinate
  1375. INT y, //@parm Mouse y coordinate
  1376. DWORD dwFlags) //@parm Mouse message wparam
  1377. {
  1378. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxRButtonDown");
  1379. if (StopMagellanScroll())
  1380. return S_OK;
  1381. CTxtSelection * psel = GetSel();
  1382. const POINT ptxy = {x, y};
  1383. POINTUV pt;
  1384. _pdp->PointuvFromPoint(pt, ptxy);
  1385. psel->CancelModes();
  1386. if(psel->PointInSel(pt) && !_fDisableDrag)
  1387. {
  1388. _fWantDrag = TRUE;
  1389. TxSetTimer(RETID_DRAGDROP, W32->GetDragDelay());
  1390. _mousePt = ptxy;
  1391. _bMouseFlags = (BYTE)dwFlags;
  1392. _fDragged = FALSE;
  1393. return S_OK;
  1394. }
  1395. return S_FALSE;
  1396. }
  1397. HRESULT CTxtEdit::OnTxMouseMove(
  1398. INT x, //@parm Mouse x coordinate
  1399. INT y, //@parm Mouse y coordinate
  1400. DWORD dwFlags, //@parm Mouse message wparam
  1401. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  1402. {
  1403. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxMouseMove");
  1404. if(!_fFocus)
  1405. return S_OK;
  1406. RECT rcxy;
  1407. TxGetClientRect(&rcxy);
  1408. if(_fWantDrag || _fCapture)
  1409. {
  1410. LONG nDragMinDist = W32->GetDragMinDist() + 3;
  1411. int dx = _mousePt.x > x ? _mousePt.x - x : x - _mousePt.x;
  1412. int dy = _mousePt.y > y ? _mousePt.y - y : y - _mousePt.y;
  1413. if(dx < nDragMinDist && dy < nDragMinDist)
  1414. {
  1415. if(!_fCapture || x > 0 && x < rcxy.right && y > 0 && y < rcxy.bottom)
  1416. {
  1417. _bMouseFlags = (BYTE)dwFlags;
  1418. return S_OK;
  1419. }
  1420. }
  1421. _fDragged = _fWantDrag;
  1422. }
  1423. _mousePt.x = x; // Remember for scrolling
  1424. _mousePt.y = y; // speed, and dir calc.
  1425. // RichEdit 1.0 allows the client to process mouse moves itself if
  1426. // we are over a link (but _not_ doing drag drop).
  1427. if(HandleLinkNotification(WM_MOUSEMOVE, 0, MAKELPARAM(x, y)))
  1428. return NOERROR;
  1429. // If we think mouse is down and it really is, do special processing
  1430. if (GetAsyncKeyState(VK_LBUTTON) < 0 ||
  1431. GetAsyncKeyState(VK_RBUTTON) < 0)
  1432. {
  1433. CTxtSelection * const psel = GetSel();
  1434. AssertSz(psel,"CTxtEdit::OnMouseMove: No selection object !");
  1435. if(_fWantDrag && !_fUsePassword &&
  1436. !IsProtected(_fReadOnly ? WM_COPY : WM_CUT, dwFlags, MAKELONG(x,y)))
  1437. {
  1438. TxKillTimer(RETID_DRAGDROP);
  1439. _ldte.StartDrag(psel, publdr);
  1440. // The mouse button may still be down, but drag drop is over
  1441. // so we need to _think_ of it as up.
  1442. _fMouseDown = FALSE;
  1443. // Similarly, OLE should have nuked the capture for us, but
  1444. // just in case something failed, release the capture.
  1445. TxSetCapture(FALSE);
  1446. _fCapture = FALSE;
  1447. }
  1448. else if(_fMouseDown) // We think mouse is down
  1449. { // and it is
  1450. POINTUV pt;
  1451. POINT ptxy = {x, y};
  1452. if(x >= rcxy.right && x < rcxy.right + 5)
  1453. ptxy.x += 5;
  1454. _pdp->PointuvFromPoint(pt, ptxy);
  1455. if(_ldte.fInDrag()) // Only drag scroll if a drag
  1456. _pdp->DragScroll(&_mousePt); // operation is in progress
  1457. psel->ExtendSelection(pt); // Extend the selection
  1458. CheckInstallContinuousScroll(); // Install srolling timer
  1459. }
  1460. }
  1461. #ifndef NOMAGELLAN
  1462. else if (!(GetAsyncKeyState(VK_MBUTTON) < 0) && !mouse.IsAutoScrolling())
  1463. {
  1464. if(_fMButtonCapture) // Ensure we aren't autoscrolling
  1465. OnTxMButtonUp (x, y, dwFlags); // via intellimouse
  1466. if(_fMouseDown)
  1467. {
  1468. // Although we thought the mouse was down, at this moment it
  1469. // clearly is not. Therefore, we pretend we got a mouse up
  1470. // message and clear our state to get ourselves back in sync
  1471. // with what is really happening.
  1472. OnTxLButtonUp(x, y, dwFlags, LB_RELEASECAPTURE);
  1473. }
  1474. }
  1475. #endif
  1476. // Either a drag was started or the mouse button was not down. In either
  1477. // case, we want no longer to start a drag so we set the flag to false.
  1478. _fWantDrag = FALSE;
  1479. return S_OK;
  1480. }
  1481. /*
  1482. * OnTxMButtonDown (x, y, dwFlags)
  1483. *
  1484. * @mfunc
  1485. * The user pressed the middle mouse button, setup to do
  1486. * continuous scrolls, which may in turn initiate a timer
  1487. * for smooth scrolling.
  1488. *
  1489. * @rdesc
  1490. * HRESULT = S_OK
  1491. */
  1492. HRESULT CTxtEdit::OnTxMButtonDown (
  1493. INT x, //@parm Mouse x coordinate
  1494. INT y, //@parm Mouse y coordinate
  1495. DWORD dwFlags) //@parm Mouse message wparam
  1496. {
  1497. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxMButtonDown");
  1498. #if !defined(NOMAGELLAN)
  1499. POINT pt = {x,y};
  1500. if(!_fFocus)
  1501. TxSetFocus();
  1502. if(!StopMagellanScroll() && mouse.MagellanStartMButtonScroll(*this, pt))
  1503. {
  1504. TxSetCapture(TRUE);
  1505. _fCapture = TRUE; // Capture the mouse
  1506. _fMouseDown = TRUE;
  1507. _fMButtonCapture = TRUE;
  1508. }
  1509. #endif
  1510. return S_OK;
  1511. }
  1512. /*
  1513. * CTxtEdit::OnTxMButtonUp (x, y, dwFlags)
  1514. *
  1515. * @mfunc
  1516. * Remove timers and capture associated with a MButtonDown
  1517. * message.
  1518. *
  1519. * @rdesc
  1520. * HRESULT = S_OK
  1521. */
  1522. HRESULT CTxtEdit::OnTxMButtonUp (
  1523. INT x, //@parm Mouse x coordinate
  1524. INT y, //@parm Mouse y coordinate
  1525. DWORD dwFlags) //@parm Mouse message wparam
  1526. {
  1527. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxMButtonUp");
  1528. #if !defined(NOMAGELLAN)
  1529. if (mouse.ContinueMButtonScroll(this, x, y))
  1530. return S_OK;
  1531. StopMagellanScroll();
  1532. #else
  1533. if(_fCapture)
  1534. TxSetCapture(FALSE);
  1535. _fCapture = FALSE;
  1536. _fMouseDown = FALSE;
  1537. _fMButtonCapture = FALSE;
  1538. #endif
  1539. return S_OK;
  1540. }
  1541. /*
  1542. * CTxtEdit::StopMagellanScroll()
  1543. *
  1544. * @mfunc
  1545. * Stops the intellimouse autoscrolling and returns
  1546. * us back into a normal state
  1547. *
  1548. * BOOL = TRUE if auto scrolling was turned off : FALSE
  1549. * Autoscrolling was never turned on
  1550. */
  1551. BOOL CTxtEdit::StopMagellanScroll ()
  1552. {
  1553. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::StopMagellanScroll");
  1554. #if !defined(NOMAGELLAN)
  1555. if (!mouse.IsAutoScrolling())
  1556. return FALSE;
  1557. mouse.MagellanEndMButtonScroll(*this);
  1558. if(_fCapture)
  1559. TxSetCapture(FALSE);
  1560. _fCapture = FALSE;
  1561. _fMouseDown = FALSE;
  1562. _fMButtonCapture = FALSE;
  1563. return TRUE;
  1564. #else
  1565. return FALSE;
  1566. #endif
  1567. }
  1568. /*
  1569. * CTxtEdit::CheckInstallContinuousScroll ()
  1570. *
  1571. * @mfunc
  1572. * There are no events that inform the app on a regular
  1573. * basis that a mouse button is down. This timer notifies
  1574. * the app that the button is still down, so that scrolling can
  1575. * continue.
  1576. */
  1577. void CTxtEdit::CheckInstallContinuousScroll ()
  1578. {
  1579. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CheckInstallContinuousScroll");
  1580. if(!_fContinuousScroll && TxSetTimer(RETID_AUTOSCROLL, cmsecScrollInterval))
  1581. _fContinuousScroll = TRUE;
  1582. }
  1583. /*
  1584. * CTxtEdit::CheckRemoveContinuousScroll ()
  1585. *
  1586. * @mfunc
  1587. * The middle mouse button, or drag button, is up
  1588. * remove the continuous scroll timer.
  1589. */
  1590. void CTxtEdit::CheckRemoveContinuousScroll ()
  1591. {
  1592. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CheckRemoveContinuousScroll");
  1593. if(_fContinuousScroll)
  1594. {
  1595. TxKillTimer(RETID_AUTOSCROLL);
  1596. _fContinuousScroll = FALSE;
  1597. }
  1598. }
  1599. /*
  1600. * OnTxTimer(idTimer)
  1601. *
  1602. * @mfunc
  1603. * Handle timers for doing background recalc and scrolling.
  1604. *
  1605. * @rdesc
  1606. * HRESULT = (idTimer valid) ? S_OK : S_FALSE
  1607. */
  1608. HRESULT CTxtEdit::OnTxTimer(
  1609. UINT idTimer)
  1610. {
  1611. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxTimer");
  1612. switch (idTimer)
  1613. {
  1614. case RETID_BGND_RECALC:
  1615. _pdp->StepBackgroundRecalc();
  1616. break;
  1617. #if !defined(NOMAGELLAN)
  1618. case RETID_MAGELLANTRACK:
  1619. mouse.TrackUpdateMagellanMButtonDown(*this, _mousePt);
  1620. break;
  1621. #endif
  1622. case RETID_AUTOSCROLL: // Continuous scrolling.
  1623. OnTxMouseMove(_mousePt.x, _mousePt.y, // Do a select drag scroll.
  1624. 0, NULL);
  1625. break;
  1626. #if !defined(NOMAGELLAN)
  1627. case RETID_SMOOTHSCROLL: // Smooth scrolling
  1628. if(_fMButtonCapture) // HACK, only 1 timer!
  1629. { // delivered on Win95
  1630. // when things get busy.
  1631. mouse.TrackUpdateMagellanMButtonDown(*this, _mousePt);
  1632. }
  1633. if(_pdp->IsSmoothVScolling()) // Test only because of
  1634. _pdp->SmoothVScrollUpdate(); // above HACK!!
  1635. break;
  1636. #endif
  1637. case RETID_DRAGDROP:
  1638. TxKillTimer(RETID_DRAGDROP);
  1639. if (_fWantDrag && _fDragged && !_fUsePassword &&
  1640. !IsProtected(_fReadOnly ? WM_COPY : WM_CUT,
  1641. _bMouseFlags, MAKELONG(_mousePt.x,_mousePt.y)))
  1642. {
  1643. IUndoBuilder * publdr;
  1644. CGenUndoBuilder undobldr(this, UB_AUTOCOMMIT, &publdr);
  1645. _ldte.StartDrag(GetSel(), publdr);
  1646. _fWantDrag = FALSE;
  1647. _fDragged = FALSE;
  1648. TxSetCapture(FALSE);
  1649. _fCapture = FALSE;
  1650. }
  1651. break;
  1652. default:
  1653. return S_FALSE;
  1654. }
  1655. return S_OK;
  1656. }
  1657. /////////////////////////// Keyboard Commands ////////////////////////////////
  1658. /*
  1659. * CTxtEdit::OnTxKeyDown(vkey, dwFlags, publdr)
  1660. *
  1661. * @mfunc
  1662. * Handle WM_KEYDOWN message
  1663. *
  1664. * @rdesc
  1665. * HRESULT with the following values:
  1666. *
  1667. * S_OK if key was understood and consumed
  1668. * S_MSG_KEY_IGNORED if key was understood, but not consumed
  1669. * S_FALSE if key was not understood or just looked at
  1670. * and in any event not consumed
  1671. */
  1672. HRESULT CTxtEdit::OnTxKeyDown(
  1673. WORD vkey, //@parm Virtual key code
  1674. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  1675. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  1676. {
  1677. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxKeyDown");
  1678. if(IN_RANGE(VK_SHIFT, vkey, VK_MENU))
  1679. {
  1680. SetKeyboardFlag(GetKbdFlags(vkey, dwFlags));
  1681. return S_FALSE;
  1682. }
  1683. BOOL fAlt = GetKeyboardFlag(ALT, VK_MENU);
  1684. BOOL fCtrl = GetKeyboardFlag(CTRL, VK_CONTROL);
  1685. BOOL fShift = GetKeyboardFlag(SHIFT, VK_SHIFT);
  1686. BOOL fRet = FALSE; // Converted to HRESULT on return
  1687. LONG nDeadKey = 0;
  1688. if(fCtrl & fShift) // Signal NonCtrl/Shift keydown
  1689. SetKeyboardFlag(LETAFTERSHIFT); // while Ctrl&Shift are down
  1690. // Handle Hebrew caps and LRM/RLM
  1691. #ifndef NOCOMPLEXSCRIPTS
  1692. if (IsBiDi())
  1693. {
  1694. if(IsBiDiCharRep(GetKeyboardCharRep(0xFFFFFFFF)))
  1695. {
  1696. _fHbrCaps = FALSE;
  1697. if(IsRich() && W32->UsingHebrewKeyboard())
  1698. {
  1699. WORD wCapital = GetKeyState(VK_CAPITAL);
  1700. _fHbrCaps = ((wCapital & 1) ^ fShift) &&
  1701. !(wCapital & 0x0080) &&
  1702. IN_RANGE('A', vkey, 'Z');
  1703. if(_fHbrCaps)
  1704. W32->ActivateKeyboard(ANSI_INDEX);
  1705. }
  1706. }
  1707. if(vkey == VK_BACK && fShift && W32->OnWin9x())
  1708. {
  1709. // Shift+Backspace generates a LRM | RLM on a BiDi keyboard.
  1710. // Consequently, we must eat the Backspace lest it delete text.
  1711. W32->_fLRMorRLM = 1;
  1712. return S_OK;
  1713. }
  1714. }
  1715. #endif
  1716. // If dragging or Alt key down, just look for ESCAPE. Note: if Alt key is
  1717. // down, we should never come here (would generate WM_SYSKEYDOWN message).
  1718. if(_fMouseDown)
  1719. {
  1720. if(vkey == VK_ESCAPE)
  1721. {
  1722. // Turn-off autoscroll.
  1723. if (StopMagellanScroll())
  1724. return S_OK;
  1725. POINT pt;
  1726. // Cancel drag select or drag & drop
  1727. GetCursorPos(&pt);
  1728. OnTxLButtonUp(pt.x, pt.y, 0, LB_RELEASECAPTURE | LB_FLUSHNOTIFY);
  1729. return S_OK;
  1730. }
  1731. return OnTxSpecialKeyDown(vkey, dwFlags, publdr);
  1732. }
  1733. CTxtSelection * const psel = GetSel();
  1734. AssertSz(psel,"CTxtEdit::OnKeyDown() - No selection object !");
  1735. if(fCtrl)
  1736. {
  1737. if(OnTxSpecialKeyDown(vkey, dwFlags, publdr) == S_OK)
  1738. return S_OK;
  1739. if(fAlt) // This following code doesn't handle
  1740. return S_FALSE; // use Ctrl+Alt, which happens for
  1741. // AltGr codes (no WM_SYSKEYDOWN)
  1742. // Shift must not be pressed for these.
  1743. if(!fShift)
  1744. {
  1745. switch(vkey)
  1746. {
  1747. case 'E':
  1748. case 'J':
  1749. case 'R':
  1750. case 'L':
  1751. {
  1752. if(!IsRich() || !IsntProtectedOrReadOnly(WM_KEYDOWN, vkey, dwFlags))
  1753. return S_FALSE;
  1754. CParaFormat PF;
  1755. PF._bAlignment = PFA_LEFT;
  1756. if (vkey == 'E')
  1757. PF._bAlignment = PFA_CENTER;
  1758. else if (vkey == 'J')
  1759. PF._bAlignment = PFA_FULL_INTERWORD;
  1760. else if (vkey == 'R')
  1761. PF._bAlignment = PFA_RIGHT;
  1762. psel->SetParaFormat(&PF, publdr, PFM_ALIGNMENT, PFM2_PARAFORMAT);
  1763. break;
  1764. }
  1765. case '1':
  1766. case '2':
  1767. case '5':
  1768. {
  1769. if(!IsRich() || !IsntProtectedOrReadOnly(WM_KEYDOWN, vkey, dwFlags))
  1770. return S_FALSE;
  1771. CParaFormat PF;
  1772. PF._bLineSpacingRule = tomLineSpaceMultiple;
  1773. PF._dyLineSpacing = (vkey - '0') * 20;
  1774. if (vkey == '5')
  1775. PF._dyLineSpacing = 30;
  1776. psel->SetParaFormat(&PF, publdr, PFM_LINESPACING, 0);
  1777. break;
  1778. }
  1779. default:
  1780. break;
  1781. }
  1782. }
  1783. switch(vkey)
  1784. {
  1785. case VK_TAB:
  1786. return OnTxChar(VK_TAB, dwFlags, publdr);
  1787. case VK_CLEAR:
  1788. case VK_NUMPAD5:
  1789. case 'A': // Ctrl-A => pselect all
  1790. psel->SelectAll();
  1791. break;
  1792. //Toggle Subscript
  1793. case 187: // =
  1794. {
  1795. if(!IsRich())
  1796. return S_FALSE;
  1797. ITextFont *pfont;
  1798. psel->GetFont(&pfont);
  1799. if (pfont)
  1800. {
  1801. pfont->SetSubscript(tomToggle);
  1802. pfont->Release();
  1803. }
  1804. }
  1805. break;
  1806. case 'C': // Ctrl-C => copy
  1807. CtrlC: CutOrCopySelection(WM_COPY, 0, 0, NULL);
  1808. break;
  1809. case 'V': // Ctrl-V => paste
  1810. CtrlV: if(IsntProtectedOrReadOnly(WM_PASTE, 0, 0))
  1811. {
  1812. PasteDataObjectToRange(NULL, (CTxtRange *)psel, 0, NULL,
  1813. publdr, PDOR_NONE);
  1814. }
  1815. break;
  1816. case 'X': // Ctrl-X => cut
  1817. CtrlX: CutOrCopySelection(WM_CUT, 0, 0, publdr);
  1818. break;
  1819. case 'Z': // Ctrl-Z => undo
  1820. PopAndExecuteAntiEvent(_pundo, 0);
  1821. break;
  1822. case 'Y': // Ctrl-Y => redo
  1823. PopAndExecuteAntiEvent(_predo, 0);
  1824. break;
  1825. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  1826. void RicheditDebugCentral(void);
  1827. case 191:
  1828. RicheditDebugCentral();
  1829. break;
  1830. #endif
  1831. #if defined(DOGFOOD)
  1832. case '1': // Shift+Ctrl+1 => start Aimm
  1833. // Activate AIMM by posting a message to RE (Shift+Ctrl+; for now)
  1834. if (fShift && _fInOurHost)
  1835. {
  1836. HWND hWnd;
  1837. TxGetWindow( &hWnd );
  1838. if (hWnd)
  1839. PostMessage(hWnd, EM_SETEDITSTYLE, SES_USEAIMM, SES_USEAIMM);
  1840. }
  1841. break;
  1842. #endif
  1843. case VK_CONTROL:
  1844. goto cont;
  1845. // English keyboard defines
  1846. #define VK_APOSTROPHE 0xDE
  1847. #define VK_GRAVE 0xC0
  1848. #define VK_SEMICOLON 0xBA
  1849. #define VK_COMMA 0xBC
  1850. #define VK_HYPHEN 0xBD
  1851. // REVIEW: restrict VK_HYPHEN to English keyboard?
  1852. case VK_HYPHEN:
  1853. return OnTxChar(fShift ? NBHYPHEN : SOFTHYPHEN, dwFlags, publdr);
  1854. case VK_SPACE:
  1855. if(!fShift)
  1856. goto cont;
  1857. return OnTxChar(NBSPACE, dwFlags, publdr);
  1858. case VK_APOSTROPHE:
  1859. if(fShift)
  1860. g_wFlags ^= KF_SMARTQUOTES;
  1861. else
  1862. nDeadKey = ACCENT_ACUTE;
  1863. break;
  1864. case VK_GRAVE:
  1865. nDeadKey = fShift ? ACCENT_TILDE : ACCENT_GRAVE;
  1866. break;
  1867. case VK_SEMICOLON:
  1868. nDeadKey = ACCENT_UMLAUT;
  1869. break;
  1870. case '6':
  1871. if(!fShift)
  1872. goto cont;
  1873. nDeadKey = ACCENT_CARET;
  1874. break;
  1875. case VK_COMMA:
  1876. nDeadKey = ACCENT_CEDILLA;
  1877. break;
  1878. default:
  1879. goto cont;
  1880. }
  1881. if(nDeadKey)
  1882. {
  1883. // Since deadkey choices vary a bit according to keyboard, we
  1884. // only enable them for English. French, German, Italian, and
  1885. // Spanish keyboards already have a fair amount of accent
  1886. // capability.
  1887. if(PRIMARYLANGID(GetKeyboardLayout(0)) == LANG_ENGLISH)
  1888. SetDeadKey((WORD)nDeadKey);
  1889. else goto cont;
  1890. }
  1891. return S_OK;
  1892. }
  1893. cont:
  1894. switch(vkey)
  1895. {
  1896. case VK_BACK:
  1897. case VK_F16:
  1898. if(_fReadOnly)
  1899. {
  1900. Beep();
  1901. fRet = TRUE;
  1902. }
  1903. else if(IsntProtectedOrReadOnly(WM_KEYDOWN, VK_BACK, dwFlags))
  1904. {
  1905. fRet = psel->Backspace(fCtrl, publdr);
  1906. }
  1907. break;
  1908. case VK_INSERT: // Ins
  1909. if(fShift) // Shift-Ins
  1910. goto CtrlV; // Alias for Ctrl-V
  1911. if(fCtrl) // Ctrl-Ins
  1912. goto CtrlC; // Alias for Ctrl-C
  1913. if(!_fReadOnly) // Ins
  1914. _fOverstrike = !_fOverstrike; // Toggle Ins/Ovr
  1915. fRet = TRUE;
  1916. break;
  1917. case VK_LEFT: // Left arrow
  1918. case VK_RIGHT: // Right arrow
  1919. fRet = (vkey == VK_LEFT) ^ (psel->GetPF()->IsRtlPara() != 0)
  1920. ? psel->Left (fCtrl, fShift)
  1921. : psel->Right(fCtrl, fShift);
  1922. break;
  1923. case VK_UP: // Up arrow
  1924. fRet = psel->Up(fCtrl, fShift);
  1925. break;
  1926. case VK_DOWN: // Down arrow
  1927. fRet = psel->Down(fCtrl, fShift);
  1928. break;
  1929. case VK_HOME: // Home
  1930. fRet = psel->Home(fCtrl, fShift);
  1931. break;
  1932. case VK_END: // End
  1933. fRet = psel->End(fCtrl, fShift);
  1934. break;
  1935. case VK_PRIOR: // PgUp
  1936. // If SystemEditMode and control is single-line, do nothing
  1937. if(!_fSystemEditMode || _pdp->IsMultiLine())
  1938. fRet = psel->PageUp(fCtrl, fShift);
  1939. break;
  1940. case VK_NEXT: // PgDn
  1941. // If SystemEditMode and control is single-line, do nothing
  1942. if(!_fSystemEditMode || _pdp->IsMultiLine())
  1943. fRet = psel->PageDown(fCtrl, fShift);
  1944. break;
  1945. case VK_DELETE: // Del
  1946. if(fShift) // Shift-Del
  1947. goto CtrlX; // Alias for Ctrl-X
  1948. if(IsntProtectedOrReadOnly(WM_KEYDOWN, VK_DELETE, dwFlags))
  1949. psel->Delete(fCtrl, publdr);
  1950. fRet = TRUE;
  1951. break;
  1952. case CONTROL('J'): // Ctrl-Return gives Ctrl-J
  1953. case VK_RETURN: // (LF), treat it as return
  1954. // If we are in 1.0 mode we need to handle <CR>'s on WM_CHAR
  1955. if(!Get10Mode())
  1956. {
  1957. fRet = InsertEOP(dwFlags, fShift, publdr);
  1958. if(!fRet)
  1959. return S_FALSE;
  1960. }
  1961. break;
  1962. default:
  1963. return S_FALSE;
  1964. }
  1965. return fRet ? S_OK : S_MSG_KEY_IGNORED;
  1966. }
  1967. /*
  1968. * CTxtEdit::InsertEOP(dwFlags, fShift, publdr)
  1969. *
  1970. * @mfunc
  1971. * Handle inserting EOPs with check for hyperlinks
  1972. *
  1973. * @rdesc
  1974. * HRESULT
  1975. */
  1976. BOOL CTxtEdit::InsertEOP(
  1977. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  1978. BOOL fShift, //@parm TRUE if Shift key depressed
  1979. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  1980. {
  1981. if(!_pdp->IsMultiLine())
  1982. {
  1983. if (!_fSystemEditMode)
  1984. Beep();
  1985. return FALSE;
  1986. }
  1987. TxSetCursor(0, NULL);
  1988. BOOL fInLink = FALSE;
  1989. if(!fShift)
  1990. HandleLinkNotification(WM_CHAR, 0, 0, &fInLink);
  1991. if(!fInLink && IsntProtectedOrReadOnly(WM_CHAR, VK_RETURN, dwFlags))
  1992. _psel->InsertEOP(publdr, (fShift && IsRich() ? VT : 0));
  1993. return TRUE;
  1994. }
  1995. /*
  1996. * CTxtEdit::CutOrCopySelection(msg, wparam, lparam, publdr)
  1997. *
  1998. * @mfunc
  1999. * Handle WM_COPY message and its keyboard hotkey aliases
  2000. *
  2001. * @rdesc
  2002. * HRESULT
  2003. */
  2004. HRESULT CTxtEdit::CutOrCopySelection(
  2005. UINT msg, //@parm Message (WM_CUT or WM_COPY)
  2006. WPARAM wparam, //@parm Message wparam for protection check
  2007. LPARAM lparam, //@parm Message lparam for protection check
  2008. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  2009. {
  2010. Assert(msg == WM_CUT || msg == WM_COPY);
  2011. if(!_fUsePassword && IsntProtectedOrReadOnly(msg, wparam, lparam))
  2012. {
  2013. CTxtSelection *psel = GetSel();
  2014. BOOL fCopy = msg == WM_COPY;
  2015. LONG lStreamFormat = psel->CheckTableSelection(fCopy, TRUE, NULL, RR_NO_LP_CHECK)
  2016. ? SFF_WRITEXTRAPAR : 0;
  2017. return fCopy
  2018. ? _ldte.CopyRangeToClipboard((CTxtRange *)psel, lStreamFormat)
  2019. : _ldte.CutRangeToClipboard((CTxtRange *)psel, lStreamFormat, publdr);
  2020. }
  2021. return NOERROR;
  2022. }
  2023. #define ENGLISH_UK MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK)
  2024. #define ENGLISH_EIRE MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE)
  2025. /*
  2026. * CTxtEdit::OnTxSpecialKeyDown(vkey, dwFlags, publdr)
  2027. *
  2028. * @mfunc
  2029. * Handle WM_KEYDOWN message for outline mode
  2030. *
  2031. * @rdesc
  2032. * HRESULT with the following values:
  2033. *
  2034. * S_OK if key was understood and consumed
  2035. * S_MSG_KEY_IGNORED if key was understood, but not consumed
  2036. * S_FALSE if key was not understood (and not consumed)
  2037. */
  2038. HRESULT CTxtEdit::OnTxSpecialKeyDown(
  2039. WORD vkey, //@parm Virtual key code
  2040. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  2041. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  2042. {
  2043. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxSpecialKeyDown");
  2044. HRESULT hr = S_FALSE; // Key not understood yet
  2045. DWORD dwKbdFlags = GetKeyboardFlags();
  2046. BOOL fUpdateFormat = TRUE;
  2047. if(!(dwKbdFlags & (CTRL | ALT))) // All hot keys here have at
  2048. return S_FALSE; // least Ctrl or Alt
  2049. if(((dwKbdFlags & ALT) || vkey != 'C' && vkey != 'V' && vkey != 'X') &&
  2050. !IsntProtectedOrReadOnly(WM_KEYDOWN, VK_BACK, dwFlags, FALSE))
  2051. {
  2052. return S_FALSE;
  2053. }
  2054. CTxtSelection * const psel = GetSel();
  2055. if(dwKbdFlags & ALT && dwKbdFlags & CTRL)
  2056. {
  2057. // AltGr generates LCTRL | RALT, so don't match hot keys with
  2058. // that combination
  2059. if(dwKbdFlags & LCTRL && dwKbdFlags & RALT)
  2060. return S_FALSE;
  2061. if(vkey == 'E')
  2062. {
  2063. LANGID lid = LANGIDFROMLCID(GetKeyboardLayout(0));
  2064. static const LANGID rgLangID[] =
  2065. {
  2066. ENGLISH_UK, ENGLISH_EIRE, LANG_POLISH, LANG_PORTUGUESE,
  2067. LANG_HUNGARIAN, LANG_VIETNAMESE
  2068. };
  2069. for(LONG i = ARRAY_SIZE(rgLangID); i--; )
  2070. {
  2071. // Don't insert Euro if lid matches any LIDs or PLIDs in rgLangID
  2072. if(lid == rgLangID[i] || PRIMARYLANGID(lid) == rgLangID[i])
  2073. return S_FALSE;
  2074. }
  2075. if(psel->PutChar(EURO, _fOverstrike, publdr))
  2076. {
  2077. SetKeyboardFlag(HOTEURO); // Setup flag to eat the next WM_CHAR w/ EURO
  2078. hr = S_OK;
  2079. }
  2080. }
  2081. else if(dwKbdFlags & SHIFT)
  2082. switch(vkey)
  2083. {
  2084. #ifdef ENABLE_OUTLINEVIEW
  2085. // FUTURE: OutlineView hot keys postponed (see below)
  2086. case 'N': // Alt-Ctrl-N => Normal View
  2087. hr = SetViewKind(VM_NORMAL);
  2088. break;
  2089. case 'O': // Alt-Ctrl-O => Outline View
  2090. hr = SetViewKind(VM_OUTLINE);
  2091. break;
  2092. #endif
  2093. case VK_F12: // Shift-Alt-Ctrl-F12 (in case Alt-X taken)
  2094. hr = psel->HexToUnicode(publdr);
  2095. break;
  2096. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  2097. case VK_F10: // Shift-Alt-Ctrl-F10
  2098. OnDumpPed();
  2099. break;
  2100. case VK_F11: // Shift-Alt-Ctrl-F11
  2101. if (W32->fDebugFont())
  2102. psel->DebugFont();
  2103. break;
  2104. #endif
  2105. }
  2106. return hr;
  2107. }
  2108. AssertSz(psel, "CTxtEdit::OnTxSpecialKeyDown() - No selection object !");
  2109. CTxtRange rg(*psel);
  2110. if(!IsRich() || !_pdp->IsMultiLine() || !(dwKbdFlags & SHIFT))
  2111. return S_FALSE;
  2112. if(dwKbdFlags & ALT) // Alt+Shift hot keys
  2113. {
  2114. // NB: Alt and Shift-Alt with _graphics_ characters generate a
  2115. // WM_SYSCHAR, which see
  2116. #ifdef ENABLE_OUTLINEVIEW
  2117. // FUTURE: These are Outline related hot keys. We will postpone these features
  2118. // since we have several bugs related to these hot keys
  2119. // Bug 5687, 5689, & 5691
  2120. switch(vkey)
  2121. {
  2122. case VK_LEFT: // Left arrow
  2123. case VK_RIGHT: // Right arrow
  2124. hr = rg.Promote(vkey == VK_LEFT ? 1 : -1, publdr);
  2125. psel->Update_iFormat(-1);
  2126. psel->Update(FALSE);
  2127. break;
  2128. case VK_UP: // Up arrow
  2129. case VK_DOWN: // Down arrow
  2130. hr = MoveSelection(vkey == VK_UP ? -1 : 1, publdr);
  2131. psel->Update(TRUE);
  2132. break;
  2133. }
  2134. #endif
  2135. return hr;
  2136. }
  2137. Assert(dwKbdFlags & CTRL && dwKbdFlags & SHIFT);
  2138. // Ctrl+Shift hot keys
  2139. switch(vkey)
  2140. {
  2141. #ifdef ENABLE_OUTLINEVIEW
  2142. // FUTUTRE: These are Outline related hot keys. We will postpone these features
  2143. // since we have several bugs related to these hot keys
  2144. // Bug 5687, 5689, & 5691
  2145. case 'N': // Demote to Body
  2146. hr = rg.Promote(0, publdr);
  2147. break;
  2148. #endif
  2149. //Toggle superscript
  2150. case 187: // =
  2151. {
  2152. ITextFont *pfont;
  2153. psel->GetFont(&pfont);
  2154. if (pfont)
  2155. {
  2156. pfont->SetSuperscript(tomToggle);
  2157. pfont->Release();
  2158. hr = S_OK;
  2159. fUpdateFormat = FALSE;
  2160. }
  2161. break;
  2162. }
  2163. case 'A':
  2164. {
  2165. ITextFont *pfont;
  2166. psel->GetFont(&pfont);
  2167. if (pfont)
  2168. {
  2169. pfont->SetAllCaps(tomToggle);
  2170. pfont->Release();
  2171. hr = S_OK;
  2172. fUpdateFormat = FALSE;
  2173. }
  2174. break;
  2175. }
  2176. case 'L': // Cycle numbering style
  2177. {
  2178. CParaFormat PF;
  2179. DWORD dwMask = PFM_NUMBERING | PFM_OFFSET;
  2180. PF._wNumbering = psel->GetPF()->_wNumbering + 1;
  2181. PF._wNumbering %= tomListNumberAsUCRoman + 1;
  2182. PF._dxOffset = 0;
  2183. if(PF._wNumbering)
  2184. {
  2185. dwMask |= PFM_NUMBERINGSTYLE | PFM_NUMBERINGSTART;
  2186. PF._wNumberingStyle = PFNS_PERIOD;
  2187. PF._wNumberingStart = 1;
  2188. PF._dxOffset = 360;
  2189. }
  2190. hr = psel->SetParaFormat(&PF, publdr, dwMask, 0);
  2191. break;
  2192. }
  2193. #define VK_RANGLE 190
  2194. #define VK_LANGLE 188
  2195. case VK_RANGLE: // '>' on US keyboards
  2196. case VK_LANGLE: // '<' on US keyboards
  2197. hr = OnSetFontSize(vkey == VK_RANGLE ? 1 : -1, 0, publdr)
  2198. ? S_OK : S_FALSE;
  2199. fUpdateFormat = (hr == S_FALSE);
  2200. break;
  2201. }
  2202. if(hr != S_FALSE)
  2203. {
  2204. if (fUpdateFormat)
  2205. psel->Update_iFormat(-1);
  2206. psel->Update(FALSE);
  2207. }
  2208. return hr;
  2209. }
  2210. /*
  2211. * CTxtEdit::OnTxChar (vkey, dwFlags, publdr)
  2212. *
  2213. * @mfunc
  2214. * Handle WM_CHAR message
  2215. *
  2216. * @rdesc
  2217. * HRESULT with the following values:
  2218. *
  2219. * S_OK if key was understood and consumed
  2220. * S_MSG_KEY_IGNORED if key was understood, but not consumed
  2221. * S_FALSE if key was not understood (and not consumed)
  2222. */
  2223. HRESULT CTxtEdit::OnTxChar(
  2224. DWORD vkey, //@parm Translated key code
  2225. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  2226. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  2227. {
  2228. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxChar");
  2229. // Reset Alt key state if needed
  2230. if (!(HIWORD(dwFlags) & KF_ALTDOWN))
  2231. ResetKeyboardFlag(ALT);
  2232. DWORD dwKbdFlags = GetKeyboardFlags();
  2233. DWORD dwFlagsPutChar = _fOverstrike | KBD_CHAR;
  2234. if(dwKbdFlags & ALTNUMPAD)
  2235. {
  2236. DWORD Number = GetKeyPadNumber();
  2237. if(Number >= 256 || vkey >= 256)
  2238. vkey = Number;
  2239. ResetKeyboardFlag(ALTNUMPAD | ALT0);
  2240. dwFlagsPutChar &= ~KBD_CHAR; // Need font binding
  2241. }
  2242. if (_fMouseDown || vkey == VK_ESCAPE || // Ctrl-Backspace generates VK_F16
  2243. vkey == VK_BACK || vkey==VK_F16) // Eat it since we process it
  2244. { // in WM_KEYDOWN
  2245. return S_OK;
  2246. }
  2247. CTxtSelection * const psel = GetSel();
  2248. AssertSz(psel,
  2249. "CTxtEdit::OnChar() - No selection object !");
  2250. if(_fReadOnly && vkey != 3) // Don't allow input if read only,
  2251. { // but allow copy (Ctrl-C)
  2252. if(vkey >= ' ')
  2253. Beep();
  2254. return S_MSG_KEY_IGNORED;
  2255. }
  2256. if(vkey >= ' ' || vkey == VK_TAB)
  2257. {
  2258. TxSetCursor(0, NULL);
  2259. if(IsntProtectedOrReadOnly(WM_CHAR, vkey, dwFlags))
  2260. {
  2261. LONG nDeadKey = GetDeadKey();
  2262. if(nDeadKey)
  2263. {
  2264. DWORD ch = vkey | 0x20; // Convert to lower case
  2265. BOOL fShift = vkey != ch; // (if ASCII letter)
  2266. // a b c d e f g h i j
  2267. const static WORD chOff[] = {0xDF, 0, 0xE7, 0, 0xE7, 0, 0, 0, 0xEB, 0,
  2268. // k l m n o p q r s t u
  2269. 0, 0, 0, 0xF1, 0xF1, 0, 0, 0, 0, 0, 0xF8};
  2270. SetDeadKey(0);
  2271. if(!IN_RANGE('a', ch, 'u')) // Not relevant ASCII
  2272. return S_OK; // letter
  2273. vkey = chOff[ch - 'a']; // Translate to base char
  2274. if(!vkey) // No accents available
  2275. return S_OK; // in current approach
  2276. if(ch == 'n')
  2277. {
  2278. if(nDeadKey != ACCENT_TILDE)
  2279. return S_OK;
  2280. }
  2281. else if(nDeadKey == ACCENT_CEDILLA)
  2282. {
  2283. if(ch != 'c')
  2284. return S_OK;
  2285. }
  2286. else // aeiou
  2287. {
  2288. vkey += (WORD)nDeadKey;
  2289. if (nDeadKey >= ACCENT_TILDE && // eiu with ~ or :
  2290. (vkey == 0xF0 || vkey & 8))
  2291. {
  2292. if(nDeadKey != ACCENT_UMLAUT)// Only have umlauts
  2293. return S_OK;
  2294. vkey--;
  2295. }
  2296. }
  2297. if(fShift) // Convert to upper case
  2298. vkey &= ~0x20;
  2299. }
  2300. // If character is LRM | RLM character, then convert vkey
  2301. if(W32->_fLRMorRLM && IsBiDi() && IN_RANGE(0xFD, vkey, 0xFE))
  2302. vkey = LTRMARK + (vkey - 0xFD);
  2303. if(dwKbdFlags & CTRL)
  2304. dwFlagsPutChar |= KBD_CTRL; // Need for Ctrl+TAB in tables
  2305. psel->PutChar(vkey, dwFlagsPutChar, publdr,
  2306. GetAdjustedTextLength() ? 0 : LOWORD(GetKeyboardLayout(0xFFFFFFFF)));
  2307. }
  2308. }
  2309. else if(Get10Mode() && (vkey == VK_RETURN || vkey == CONTROL('J')))
  2310. InsertEOP(dwFlags, FALSE, publdr); // 1.0 handled <CR> on WM_CHAR
  2311. #ifndef NOCOMPLEXSCRIPTS
  2312. if(_fHbrCaps)
  2313. {
  2314. W32->ActivateKeyboard(HEBREW_INDEX);
  2315. _fHbrCaps = FALSE;
  2316. }
  2317. #endif
  2318. return S_OK;
  2319. }
  2320. /*
  2321. * CTxtEdit::OnTxSysChar (vkey, dwFlags, publdr)
  2322. *
  2323. * @mfunc
  2324. * Handle WM_SYSCHAR message
  2325. *
  2326. * @rdesc
  2327. * HRESULT with the following values:
  2328. *
  2329. * S_OK if key was understood and consumed
  2330. * S_MSG_KEY_IGNORED if key was understood, but not consumed
  2331. * S_FALSE if key was not understood (and not consumed)
  2332. */
  2333. HRESULT CTxtEdit::OnTxSysChar(
  2334. WORD vkey, //@parm Translated key code
  2335. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  2336. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  2337. {
  2338. if(!(HIWORD(dwFlags) & KF_ALTDOWN) ||
  2339. !IsntProtectedOrReadOnly(WM_KEYDOWN, vkey, dwFlags, FALSE))
  2340. {
  2341. return S_FALSE;
  2342. }
  2343. BOOL fWholeDoc = TRUE;
  2344. HRESULT hr = S_FALSE;
  2345. int level = 0;
  2346. CTxtSelection * const psel = GetSel();
  2347. switch(vkey)
  2348. {
  2349. case VK_BACK:
  2350. return S_OK;
  2351. case 'x':
  2352. hr = psel->HexToUnicode(publdr);
  2353. break;
  2354. case 'X':
  2355. hr = psel->UnicodeToHex(publdr);
  2356. break;
  2357. case '+':
  2358. case '-':
  2359. level = vkey == VK_ADD ? 1 : -1;
  2360. fWholeDoc = FALSE;
  2361. /* Fall through */
  2362. case 'A':
  2363. case '1':
  2364. case '2':
  2365. case '3':
  2366. case '4':
  2367. case '5':
  2368. case '6':
  2369. case '7':
  2370. case '8':
  2371. case '9':
  2372. {
  2373. CTxtRange rg(*psel);
  2374. if(!level)
  2375. level = vkey == 'A' ? 9 : vkey - '0';
  2376. return rg.ExpandOutline(level, fWholeDoc);
  2377. }
  2378. }
  2379. return hr;
  2380. }
  2381. HRESULT CTxtEdit::OnTxSysKeyDown(
  2382. WORD vkey, //@parm Virtual key code
  2383. DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
  2384. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  2385. {
  2386. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxSysKeyDown");
  2387. if(IN_RANGE(VK_SHIFT, vkey, VK_MENU))
  2388. {
  2389. SetKeyboardFlag(GetKbdFlags(vkey, dwFlags));
  2390. SetKeyPadNumber(0); // Init keypad number to 0
  2391. return S_FALSE;
  2392. }
  2393. if (StopMagellanScroll())
  2394. return S_FALSE;
  2395. HRESULT hr = OnTxSpecialKeyDown(vkey, dwFlags, publdr);
  2396. if(hr != S_FALSE)
  2397. return hr;
  2398. if(vkey == VK_BACK && (HIWORD(dwFlags) & KF_ALTDOWN))
  2399. {
  2400. if(PopAndExecuteAntiEvent(_pundo, 0) != NOERROR)
  2401. hr = S_MSG_KEY_IGNORED;
  2402. }
  2403. else if(vkey == VK_F10 && // F10
  2404. !(HIWORD(dwFlags) & KF_REPEAT) && // Key previously up
  2405. (GetKeyboardFlags() & SHIFT)) // Shift is down
  2406. {
  2407. HandleKbdContextMenu();
  2408. }
  2409. return hr;
  2410. }
  2411. /////////////////////////////// Other system events //////////////////////////////
  2412. HRESULT CTxtEdit::OnContextMenu(LPARAM lparam)
  2413. {
  2414. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnContextMenu");
  2415. POINT pt;
  2416. pt.x = LOWORD(lparam);
  2417. pt.y = HIWORD(lparam);
  2418. if(TxScreenToClient(&pt))
  2419. return OnTxRButtonUp(pt.x, pt.y, 0, RB_NOSELCHECK);
  2420. return S_FALSE;
  2421. }
  2422. /*
  2423. * CTxtEdit::HandleKbdContextMenu ()
  2424. *
  2425. * @mfunc decides where to put the context menu on the basis of where the
  2426. * the selection is. Useful for shift-F10 and VK_APPS, where
  2427. * we aren't given a location.
  2428. */
  2429. void CTxtEdit::HandleKbdContextMenu()
  2430. {
  2431. POINTUV pt;
  2432. RECTUV rc;
  2433. const CTxtSelection * const psel = GetSel();
  2434. int RbOption = RB_DEFAULT;
  2435. // Figure out where selection ends and put context menu near it
  2436. if(_pdp->PointFromTp(*psel, NULL, FALSE, pt, NULL, TA_TOP) < 0)
  2437. return;
  2438. // Due to various factors, the result of PointFromTp doesn't land
  2439. // in the selection in PointInSel. Therefore, we send in an override
  2440. // here if the selection is non-degenerate and to force the result
  2441. // and thus have the correct context menu appear.
  2442. LONG cpMin;
  2443. LONG cpMost;
  2444. psel->GetRange(cpMin, cpMost);
  2445. if (cpMin != cpMost)
  2446. {
  2447. RbOption = RB_FORCEINSEL;
  2448. }
  2449. // Make sure point is still within bounds of edit control
  2450. _pdp->GetViewRect(rc);
  2451. //REVIEW (keithcu) What is this +2/-2???
  2452. if (pt.u < rc.left)
  2453. pt.u = rc.left;
  2454. if (pt.u > rc.right - 2)
  2455. pt.u = rc.right - 2;
  2456. if (pt.v < rc.top)
  2457. pt.v = rc.top;
  2458. if (pt.v > rc.bottom - 2)
  2459. pt.v = rc.bottom - 2;
  2460. POINT ptxy;
  2461. _pdp->PointFromPointuv(ptxy, pt);
  2462. OnTxRButtonUp(ptxy.x, ptxy.y, 0, RbOption);
  2463. }
  2464. /////////////////////////////// Format Range Commands //////////////////////////////
  2465. /*
  2466. * CTxtEdit::OnFormatRange (pfr, prtcon, hdcMeasure,
  2467. * xMeasurePerInch, yMeasurePerInch)
  2468. * @mfunc
  2469. * Format the range given by pfr
  2470. *
  2471. * @comm
  2472. * This function inputs API cp's that may differ from the
  2473. * corresponding internal Unicode cp's.
  2474. */
  2475. LRESULT CTxtEdit::OnFormatRange(
  2476. FORMATRANGE * pfr,
  2477. SPrintControl prtcon,
  2478. BOOL fSetupDC)
  2479. {
  2480. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnFormatRange");
  2481. LONG cpMin = 0;
  2482. LONG cpMost = 0;
  2483. if(pfr)
  2484. {
  2485. cpMin = GetCpFromAcp(pfr->chrg.cpMin);
  2486. cpMost = GetCpFromAcp(pfr->chrg.cpMost);
  2487. }
  2488. // Even if there is 0 text, we want to print the control so that it will
  2489. // fill the control with background color.
  2490. // Use Adjusted Text Length. Embedded objects using RichEdit will get the empty
  2491. // document they expect and will create a default size document.
  2492. if(!pfr || cpMin >= GetAdjustedTextLength() &&
  2493. !prtcon._fPrintFromDraw)
  2494. { // We're done formatting, get rid of our printer's display context.
  2495. delete _pdpPrinter;
  2496. _pdpPrinter = NULL;
  2497. return GetAcpFromCp(GetAdjustedTextLength());
  2498. }
  2499. LONG cpReturn = -1;
  2500. BOOL fSetDCWorked = FALSE;
  2501. // Fix MFC Print preview in mirrored control
  2502. //
  2503. // MFC CPreviewView sends us a mirrored rendering DC. We need to disable
  2504. // this mirroring effect so our internal state remains consistent with user
  2505. // action. We also need to disable mirrored window mode in CPreviewView
  2506. // window. [wchao - 4/9/1999]
  2507. //
  2508. HDC hdcLocal = pfr->hdc;
  2509. #ifndef NOCOMPLEXSCRIPTS
  2510. DWORD dwLayout = W32GetLayout(hdcLocal);
  2511. if (dwLayout & LAYOUT_RTL)
  2512. {
  2513. HWND hwndView = WindowFromDC(hdcLocal);
  2514. if (hwndView)
  2515. {
  2516. DWORD dwExStyleView = GetWindowLong(hwndView, GWL_EXSTYLE);
  2517. if (dwExStyleView & WS_EX_LAYOUTRTL)
  2518. SetWindowLong(hwndView, GWL_EXSTYLE, dwExStyleView & ~WS_EX_LAYOUTRTL);
  2519. }
  2520. W32SetLayout(hdcLocal, 0);
  2521. }
  2522. #endif
  2523. // First time in with this printer, set up a new display context.
  2524. // IMPORTANT: proper completion of the printing process is required
  2525. // to dispose of this context and begin a new context.
  2526. // This is implicitly done by printing the last character, or
  2527. // sending an EM_FORMATRANGE message with pfr equal to NULL.
  2528. if(!_pdpPrinter)
  2529. {
  2530. _pdpPrinter = new CDisplayPrinter (this, hdcLocal, &pfr->rc, prtcon);
  2531. _pdpPrinter->Init();
  2532. _pdpPrinter->SetWordWrap(TRUE);
  2533. // Future: (ricksa) This is a really yucky way to pass the draw info
  2534. // to the printer but it was quick. We want to make this better.
  2535. _pdpPrinter->ResetDrawInfo(_pdp);
  2536. // Set temporary zoom factor (if there is one).
  2537. _pdpPrinter->SetTempZoomDenominator(_pdp->GetTempZoomDenominator());
  2538. }
  2539. else
  2540. _pdpPrinter->SetPrintDimensions(&pfr->rc);
  2541. LONG dxpInch = 0, dypInch = 0;
  2542. // We set the DC everytime because it could have changed.
  2543. if(GetDeviceCaps(hdcLocal, TECHNOLOGY) != DT_METAFILE)
  2544. {
  2545. // This is not a metafile so do the normal thing
  2546. fSetDCWorked = _pdpPrinter->SetDC(hdcLocal);
  2547. }
  2548. else
  2549. {
  2550. //Forms^3 draws using screen resolution, while OLE specifies HIMETRIC
  2551. dxpInch = fInOurHost() ? 2540 : W32->GetXPerInchScreenDC();
  2552. dypInch = fInOurHost() ? 2540 : W32->GetYPerInchScreenDC();
  2553. if (!fSetupDC)
  2554. {
  2555. RECT rc;
  2556. rc.left = MulDiv(pfr->rcPage.left, dxpInch, LX_PER_INCH);
  2557. rc.right = MulDiv(pfr->rcPage.right, dxpInch, LX_PER_INCH);
  2558. rc.top = MulDiv(pfr->rcPage.top, dypInch, LY_PER_INCH);
  2559. rc.bottom = MulDiv(pfr->rcPage.bottom, dypInch, LY_PER_INCH);
  2560. SetWindowOrgEx(hdcLocal, rc.left, rc.top, NULL);
  2561. SetWindowExtEx(hdcLocal, rc.right, rc.bottom, NULL);
  2562. }
  2563. _pdpPrinter->SetMetafileDC(hdcLocal, dxpInch, dypInch);
  2564. fSetDCWorked = TRUE;
  2565. }
  2566. if(fSetDCWorked)
  2567. {
  2568. //It is illogical to have the target device be the screen and the presentation
  2569. //device be a HIMETRIC metafile.
  2570. LONG dxpInchT = -1, dypInchT = -1;
  2571. if (dxpInch && GetDeviceCaps(pfr->hdcTarget, TECHNOLOGY) == DT_RASDISPLAY)
  2572. {
  2573. dxpInchT = dxpInch;
  2574. dypInchT = dypInch;
  2575. }
  2576. // We set this every time because it could have changed.
  2577. if(_pdpPrinter->SetTargetDC(pfr->hdcTarget, dxpInchT, dypInchT))
  2578. {
  2579. // Format another, single page worth of text.
  2580. cpReturn = _pdpPrinter->FormatRange(cpMin, cpMost, prtcon._fDoPrint);
  2581. if(!prtcon._fPrintFromDraw)
  2582. {
  2583. // After formatting, we know where the bottom is. But we only
  2584. // want to set this if we are writing a page rather than
  2585. // displaying a control on the printer.
  2586. pfr->rc.bottom = pfr->rc.top + _pdpPrinter->DYtoLY(_pdpPrinter->GetHeight());
  2587. }
  2588. //REVIEW (keithcu) What to do here?
  2589. // Remember this in case the host wishes to do its own banding.
  2590. _pdpPrinter->SetPrintView(pfr->rc); // we need to save this for OnDisplayBand.
  2591. _pdpPrinter->SetPrintPage(pfr->rcPage);
  2592. // If we're asked to render, then render the entire page in one go.
  2593. if(prtcon._fDoPrint && (cpReturn > 0 || prtcon._fPrintFromDraw))
  2594. {
  2595. OnDisplayBand(&pfr->rc, prtcon._fPrintFromDraw);
  2596. // Note: we can no longer call OnDisplayBand without reformatting.
  2597. _pdpPrinter->DeleteSubLayouts(0, -1);
  2598. _pdpPrinter->Clear(AF_DELETEMEM);
  2599. }
  2600. }
  2601. }
  2602. return cpReturn > 0 ? GetAcpFromCp(cpReturn) : cpReturn;
  2603. }
  2604. BOOL CTxtEdit::OnDisplayBand(
  2605. const RECT *prcView,
  2606. BOOL fPrintFromDraw)
  2607. {
  2608. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnDisplayBand");
  2609. HDC hdcPrinter;
  2610. RECT rcView, rcPrint;
  2611. RECTUV rcuvView, rcuvPrint;
  2612. // Make sure OnFormatRange was called and that it actually rendered something.
  2613. if(!_pdpPrinter || !_pdpPrinter->Count())
  2614. return FALSE;
  2615. // Proportionally map to printers extents.
  2616. _pdpPrinter->LRtoDR(rcView, *prcView, _pdpPrinter->GetTflow());
  2617. rcPrint = _pdpPrinter->GetPrintView();
  2618. _pdpPrinter->LRtoDR(rcPrint, rcPrint, _pdpPrinter->GetTflow());
  2619. _pdpPrinter->RectuvFromRect(rcuvPrint, rcPrint);
  2620. _pdpPrinter->RectuvFromRect(rcuvView, rcView);
  2621. // Get printer DC because we use it below.
  2622. hdcPrinter = _pdpPrinter->GetDC();
  2623. if(fPrintFromDraw)
  2624. {
  2625. // We need to take view inset into account
  2626. _pdpPrinter->GetViewRect(rcuvPrint, &rcuvPrint);
  2627. }
  2628. // Render this band (if there's something to render)
  2629. if(rcuvView.top < rcuvView.bottom)
  2630. _pdpPrinter->Render(rcuvPrint, rcuvView);
  2631. return TRUE;
  2632. }
  2633. //////////////////////////////// Protected ranges //////////////////////////////////
  2634. /*
  2635. * CTxtEdit::IsProtected (msg, wparam, lparam)
  2636. *
  2637. * @mfunc
  2638. * Find out if selection is protected
  2639. *
  2640. * @rdesc
  2641. * TRUE iff 1) control is read-only or 2) selection is protected and
  2642. * parent query says to protect
  2643. */
  2644. BOOL CTxtEdit::IsProtected(
  2645. UINT msg, //@parm Message id
  2646. WPARAM wparam, //@parm WPARAM from window's message
  2647. LPARAM lparam) //@parm LPARAM from window's message
  2648. {
  2649. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::IsProtected");
  2650. CHECKPROTECT chkprot = CHKPROT_EITHER;
  2651. CTxtSelection *psel = GetSel();
  2652. if(!psel)
  2653. return FALSE;
  2654. // There are a few special cases to consider, namely backspacing
  2655. // into a protected range, deleting into a protected range, and type
  2656. // with overstrike into a protected range.
  2657. if(msg == WM_KEYDOWN && (wparam == VK_BACK || wparam == VK_F16))
  2658. {
  2659. // Check for format behind selection, if we are trying to
  2660. // backspace an insertion point.
  2661. chkprot = CHKPROT_BACKWARD;
  2662. }
  2663. else if(msg == WM_KEYDOWN && wparam == VK_DELETE ||
  2664. _fOverstrike && msg == WM_CHAR)
  2665. {
  2666. chkprot = CHKPROT_FORWARD;
  2667. }
  2668. // HACK ALERT: we don't do fIsDBCS protection checking for EM_REPLACESEL,
  2669. // EM_SETCHARFORMAT, or EM_SETPARAFORMAT. Outlook uses these APIs
  2670. // extensively and DBCS protection checking messes them up. N.B. the
  2671. // following if statement assumes that IsProtected returns a tri-value.
  2672. PROTECT iProt = psel->IsProtected(chkprot);
  2673. if (iProt == PROTECTED_YES && msg != EM_REPLACESEL &&
  2674. msg != EM_SETCHARFORMAT && msg != EM_SETPARAFORMAT ||
  2675. iProt == PROTECTED_ASK && _dwEventMask & ENM_PROTECTED &&
  2676. QueryUseProtection(psel, msg, wparam, lparam))
  2677. {
  2678. return TRUE;
  2679. }
  2680. return FALSE;
  2681. }
  2682. /*
  2683. * CTxtEdit::IsntProtectedOrReadOnly (msg, wparam, lparam, BOOL)
  2684. *
  2685. * @mfunc
  2686. * Find out if selection isn't protected or read only. If it is,
  2687. * ring bell. For msg = WM_COPY, only protection is checked.
  2688. *
  2689. * @rdesc
  2690. * TRUE iff 1) control isn't read-only and 2) selection either isn't
  2691. * protected or parent query says not to protect
  2692. *
  2693. * @devnote This function is useful for UI operations (like typing).
  2694. */
  2695. BOOL CTxtEdit::IsntProtectedOrReadOnly(
  2696. UINT msg, //@parm Message
  2697. WPARAM wparam, //@parm Corresponding wparam
  2698. LPARAM lparam, //@parm Corresponding lparam
  2699. BOOL fBeep) //@parm OK to beep
  2700. {
  2701. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::IsProtectedOrReadOnly");
  2702. if (!IsProtected(msg, wparam, lparam) &&
  2703. (msg == WM_COPY || !_fReadOnly)) // WM_COPY only cares about
  2704. { // protection
  2705. return TRUE;
  2706. }
  2707. if (fBeep)
  2708. Beep();
  2709. return FALSE;
  2710. }
  2711. /*
  2712. * CTxtEdit::IsProtectedRange (msg, wparam, lparam, prg)
  2713. *
  2714. * @mfunc
  2715. * Find out if range prg is protected
  2716. *
  2717. * @rdesc
  2718. * TRUE iff control is read-only or range is protected and parent
  2719. * query says to protect
  2720. */
  2721. BOOL CTxtEdit::IsProtectedRange(
  2722. UINT msg, //@parm Message id
  2723. WPARAM wparam, //@parm WPARAM from window's message
  2724. LPARAM lparam, //@parm LPARAM from window's message
  2725. CTxtRange * prg) //@parm Range to examine
  2726. {
  2727. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::IsProtectedRange");
  2728. PROTECT iProt = prg->IsProtected(CHKPROT_EITHER);
  2729. if (iProt == PROTECTED_YES ||
  2730. (iProt == PROTECTED_ASK &&
  2731. (_dwEventMask & ENM_PROTECTED) &&
  2732. QueryUseProtection(prg, msg, wparam, lparam)))
  2733. // N.B. the preceding if statement assumes that IsProtected returns a tri-value
  2734. {
  2735. return TRUE;
  2736. }
  2737. return FALSE;
  2738. }
  2739. /*
  2740. * RegisterTypeLibrary
  2741. *
  2742. * @mfunc
  2743. * Auxiliary function to ensure the type library is registered if Idispatch is used.
  2744. */
  2745. void RegisterTypeLibrary( void )
  2746. {
  2747. #ifndef NOREGISTERTYPELIB
  2748. static BOOL fOnce = FALSE;
  2749. if (!fOnce)
  2750. {
  2751. CLock Lock;
  2752. fOnce = TRUE;
  2753. HRESULT hRes = NOERROR;
  2754. WCHAR szModulePath[MAX_PATH];
  2755. ITypeLib *pTypeLib = NULL;
  2756. // Obtain the path to this module's executable file
  2757. W32->GetModuleFileName( hinstRE, szModulePath, MAX_PATH );
  2758. // Load and register the type library resource
  2759. if (LoadRegTypeLib(LIBID_tom, 1, 0, LANG_NEUTRAL, &pTypeLib) != NOERROR)
  2760. {
  2761. hRes = W32->LoadTypeLibEx(szModulePath, REGKIND_REGISTER, &pTypeLib);
  2762. }
  2763. if(SUCCEEDED(hRes) && pTypeLib)
  2764. {
  2765. pTypeLib->Release();
  2766. }
  2767. }
  2768. #endif
  2769. }
  2770. /////////////////////////////// Private IUnknown //////////////////////////////
  2771. HRESULT __stdcall CTxtEdit::CUnknown::QueryInterface(
  2772. REFIID riid,
  2773. void **ppvObj)
  2774. {
  2775. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CUnknown::QueryInterface");
  2776. CTxtEdit *ped = (CTxtEdit *)GETPPARENT(this, CTxtEdit, _unk);
  2777. *ppvObj = NULL;
  2778. if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITextServices))
  2779. *ppvObj = (ITextServices *)ped;
  2780. else if(IsEqualIID(riid, IID_IDispatch))
  2781. {
  2782. *ppvObj = (IDispatch *)ped;
  2783. RegisterTypeLibrary();
  2784. }
  2785. else if(IsEqualIID(riid, IID_ITextDocument))
  2786. {
  2787. *ppvObj = (ITextDocument *)ped;
  2788. // No need to do this. It was put in for Alpha thunking.
  2789. // A better thing to do is to force clients who need this
  2790. // to QI for IDispatch before QI for ITextDocument
  2791. // RegisterTypeLibrary();
  2792. }
  2793. else if(IsEqualIID(riid, IID_ITextDocument2))
  2794. *ppvObj = (ITextDocument2 *)ped;
  2795. else if(IsEqualIID(riid, IID_IRichEditOle))
  2796. *ppvObj = (IRichEditOle *)ped;
  2797. else if(IsEqualIID(riid, IID_IRichEditOleCallback))
  2798. {
  2799. // NB!! Returning this pointer in our QI is
  2800. // phenomenally bogus; it breaks fundamental COM
  2801. // identity rules (granted, not many understand them!).
  2802. // Anyway, RichEdit 1.0 did this, so we better.
  2803. TRACEWARNSZ("Returning IRichEditOleCallback interface, COM "
  2804. "identity rules broken!");
  2805. *ppvObj = ped->GetRECallback();
  2806. }
  2807. if(*ppvObj)
  2808. {
  2809. ((IUnknown *) *ppvObj)->AddRef();
  2810. return S_OK;
  2811. }
  2812. return E_NOINTERFACE;
  2813. }
  2814. ULONG __stdcall CTxtEdit::CUnknown::AddRef()
  2815. {
  2816. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CUnknown::AddRef");
  2817. return ++_cRefs;
  2818. }
  2819. ULONG __stdcall CTxtEdit::CUnknown::Release()
  2820. {
  2821. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::CUnknown::Release");
  2822. // the call manager will take care of deleting our instance if appropriate.
  2823. CTxtEdit *ped = GETPPARENT(this, CTxtEdit, _unk);
  2824. CCallMgr callmgr(ped);
  2825. ULONG culRefs = --_cRefs;
  2826. if(culRefs == 0)
  2827. {
  2828. // Even though we don't delete ourselves now, dump the callback
  2829. // if we have it. This make implementation a bit easier on clients.
  2830. if(ped->_pobjmgr)
  2831. ped->_pobjmgr->SetRECallback(NULL);
  2832. // Make sure our timers are gone
  2833. ped->TxKillTimer(RETID_AUTOSCROLL);
  2834. ped->TxKillTimer(RETID_DRAGDROP);
  2835. ped->TxKillTimer(RETID_BGND_RECALC);
  2836. ped->TxKillTimer(RETID_SMOOTHSCROLL);
  2837. ped->TxKillTimer(RETID_MAGELLANTRACK);
  2838. }
  2839. return culRefs;
  2840. }
  2841. /*
  2842. * ValidateTextRange(pstrg)
  2843. *
  2844. * @func
  2845. * Makes sure that an input text range structure makes sense.
  2846. *
  2847. * @rdesc
  2848. * Size of the buffer required to accept copy of data or -1 if all the
  2849. * data in the control is requested.
  2850. *
  2851. * @comm
  2852. * This is used both in this file and in the RichEditANSIWndProc
  2853. */
  2854. LONG ValidateTextRange(
  2855. TEXTRANGE *pstrg) //@parm pointer to a text range structure
  2856. {
  2857. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "ValidateTextRange");
  2858. // Validate that the input structure makes sense. In the first
  2859. // place it must be big enough. Secondly, the values must sense.
  2860. // Remember that if the cpMost field is -1 and the cpMin field
  2861. // is 0 this means that the call wants the entire buffer.
  2862. if (IsBadReadPtr(pstrg, sizeof(TEXTRANGE)) ||
  2863. ((pstrg->chrg.cpMost < 1 || pstrg->chrg.cpMin < 0 ||
  2864. pstrg->chrg.cpMost <= pstrg->chrg.cpMin) &&
  2865. !(pstrg->chrg.cpMost == -1 && !pstrg->chrg.cpMin)))
  2866. {
  2867. // This isn't valid so tell the caller we didn't copy any data
  2868. return 0;
  2869. }
  2870. // Calculate size of buffer that we need on return
  2871. return pstrg->chrg.cpMost - pstrg->chrg.cpMin;
  2872. }
  2873. //////////////////////////////////// Selection /////////////////////////////////////
  2874. CTxtSelection * CTxtEdit::GetSel()
  2875. {
  2876. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetSel");
  2877. if(!_psel && _pdp)
  2878. {
  2879. // There is no selection object available so create it.
  2880. _psel = new CTxtSelection(_pdp);
  2881. if(_psel)
  2882. _psel->AddRef(); // Set reference count = 1
  2883. }
  2884. // It is caller's responsiblity to notice that an error occurred
  2885. // in allocation of selection object.
  2886. return _psel;
  2887. }
  2888. void CTxtEdit::DiscardSelection()
  2889. {
  2890. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::DiscardSelection");
  2891. if(_psel)
  2892. {
  2893. _psel->Release();
  2894. if(_psel)
  2895. {
  2896. // The text services reference is not the last reference to the
  2897. // selection. We could keep track of the fact that text services
  2898. // has released its reference and when text services gets a
  2899. // reference again, do the AddRef there so that if the last
  2900. // reference went away while we were still inactive, the selection
  2901. // object would go away. However, it is seriously doubtful that
  2902. // such a case will be very common. Therefore, just do the simplest
  2903. // thing and put our reference back.
  2904. _psel->AddRef();
  2905. }
  2906. }
  2907. }
  2908. void CTxtEdit::GetSelRangeForRender(
  2909. LONG *pcpSelMin,
  2910. LONG *pcpSelMost)
  2911. {
  2912. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetSelRangeForRender");
  2913. // If we have no selection or we are not active and the selection
  2914. // has been requested to be hidden, there is no selection so we
  2915. // just return 0's.
  2916. if(!_psel || (!_fInPlaceActive && _fHideSelection))
  2917. {
  2918. *pcpSelMin = 0;
  2919. *pcpSelMost = 0;
  2920. return;
  2921. }
  2922. // Otherwise return the state of the current selection.
  2923. *pcpSelMin = _psel->GetScrSelMin();
  2924. *pcpSelMost = _psel->GetScrSelMost();
  2925. }
  2926. LRESULT CTxtEdit::OnGetSelText(
  2927. WCHAR *psz)
  2928. {
  2929. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnGetSelText");
  2930. LONG cpMin = GetSelMin(); // length + 1 for the null
  2931. LONG cpMost = GetSelMost();
  2932. return GetTextRange(cpMin, cpMost - cpMin + 1, psz);
  2933. }
  2934. /*
  2935. * CTxtEdit::OnExGetSel (pcrSel)
  2936. *
  2937. * @mfunc
  2938. * Get the current selection acpMin, acpMost packaged in a CHARRANGE.
  2939. *
  2940. * @comm
  2941. * This function outputs API cp's that may differ from the
  2942. * corresponding internal Unicode cp's.
  2943. */
  2944. void CTxtEdit::OnExGetSel(
  2945. CHARRANGE *pcrSel) //@parm Output parm to receive acpMin, acpMost
  2946. {
  2947. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnExGetSel");
  2948. pcrSel->cpMin = GetAcpFromCp(GetSelMin());
  2949. pcrSel->cpMost = GetAcpFromCp(GetSelMost());
  2950. }
  2951. /*
  2952. * CTxtEdit::OnGetSel (pacpMin, pacpMost)
  2953. *
  2954. * @mfunc
  2955. * Get the current selection acpMin, acpMost.
  2956. *
  2957. * @rdesc
  2958. * LRESULT = acpMost > 65535L ? -1 : MAKELRESULT(acpMin, acpMost)
  2959. *
  2960. * @comm
  2961. * This function outputs API cp's that may differ from the
  2962. * corresponding internal Unicode cp's.
  2963. */
  2964. LRESULT CTxtEdit::OnGetSel(
  2965. LONG *pacpMin, //@parm Output parm to receive acpMin
  2966. LONG *pacpMost) //@parm Output parm to receive acpMost
  2967. {
  2968. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnGetSel");
  2969. CHARRANGE crSel;
  2970. OnExGetSel(&crSel);
  2971. if(pacpMin)
  2972. *pacpMin = crSel.cpMin;
  2973. if(pacpMost)
  2974. *pacpMost = crSel.cpMost;
  2975. return (crSel.cpMost > 65535l) ? (LRESULT) -1
  2976. : MAKELRESULT((WORD) crSel.cpMin, (WORD) crSel.cpMost);
  2977. }
  2978. /*
  2979. * CTxtEdit::OnSetSel (acpMin, acpMost)
  2980. *
  2981. * @mfunc
  2982. * Implements the EM_SETSEL message
  2983. *
  2984. * Algorithm:
  2985. * There are three basic cases to handle
  2986. *
  2987. * cpMin < 0, cpMost ??? -- Collapse selection to insertion point
  2988. * at text end if cpMost < 0 and else at
  2989. * selection active end
  2990. * cpMin >= 0, cpMost < 0 -- select from cpMin to text end with
  2991. * active end at text end
  2992. *
  2993. * cpMin >= 0, cpMost >= 0 -- Treat as cpMin, cpMost with active
  2994. * end at cpMost
  2995. *
  2996. * @comm
  2997. * This function inputs API cp's that may differ from the
  2998. * corresponding internal Unicode cp's.
  2999. */
  3000. LRESULT CTxtEdit::OnSetSel(
  3001. LONG acpMin, //@parm Input acpMin
  3002. LONG acpMost) //@parm Input acpMost
  3003. {
  3004. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetSel");
  3005. // Since this is only called from the window proc, we are always active
  3006. Assert(GetSel());
  3007. CTxtSelection * const psel = GetSel();
  3008. LONG cpMin, cpMost;
  3009. if(acpMin < 0)
  3010. cpMin = cpMost = (acpMost < 0) ? tomForward : psel->GetCp();
  3011. else
  3012. {
  3013. cpMin = GetCpFromAcp(acpMin);
  3014. cpMost = (acpMost < 0) ? tomForward : GetCpFromAcp(acpMost);
  3015. }
  3016. if(Get10Mode() && cpMost < cpMin) // In 10 mode, ensure
  3017. { // cpMost >= cpMin. In
  3018. cpMin ^= cpMost; // SetSelection, we set active
  3019. cpMost ^= cpMin; // end to cpMost, which can be
  3020. cpMin ^= cpMost; // smaller than cpMin, in spite
  3021. } // of its name.
  3022. psel->SetSelection(cpMin, cpMost);
  3023. return GetAcpFromCp(psel->GetCpMost());
  3024. }
  3025. /////////////////////////////// DROP FILES support //////////////////////////////////////
  3026. #ifndef NODROPFILES
  3027. LRESULT CTxtEdit::InsertFromFile (
  3028. LPCTSTR lpFile)
  3029. {
  3030. REOBJECT reobj;
  3031. LPRICHEDITOLECALLBACK const precall = GetRECallback();
  3032. HRESULT hr = NOERROR;
  3033. if(!precall)
  3034. return E_NOINTERFACE;
  3035. ZeroMemory(&reobj, sizeof(REOBJECT));
  3036. reobj.cbStruct = sizeof(REOBJECT);
  3037. // Get storage for the object from client
  3038. hr = precall->GetNewStorage(&reobj.pstg);
  3039. if(hr)
  3040. {
  3041. TRACEERRORSZ("GetNewStorage() failed.");
  3042. goto err;
  3043. }
  3044. // Create an object site for new object
  3045. hr = GetClientSite(&reobj.polesite);
  3046. if(!reobj.polesite)
  3047. {
  3048. TRACEERRORSZ("GetClientSite() failed.");
  3049. goto err;
  3050. }
  3051. hr = OleCreateLinkToFile(lpFile, IID_IOleObject, OLERENDER_DRAW,
  3052. NULL, NULL, reobj.pstg, (LPVOID*)&reobj.poleobj);
  3053. if(hr)
  3054. {
  3055. TRACEERRORSZ("Failure creating link object.");
  3056. goto err;
  3057. }
  3058. reobj.cp = REO_CP_SELECTION;
  3059. reobj.dvaspect = DVASPECT_CONTENT;
  3060. //Get object clsid
  3061. hr = reobj.poleobj->GetUserClassID(&reobj.clsid);
  3062. if(hr)
  3063. {
  3064. TRACEERRORSZ("GetUserClassID() failed.");
  3065. goto err;
  3066. }
  3067. // Let client know what we're up to
  3068. hr = precall->QueryInsertObject(&reobj.clsid, reobj.pstg,
  3069. REO_CP_SELECTION);
  3070. if(hr != NOERROR)
  3071. {
  3072. TRACEERRORSZ("QueryInsertObject() failed.");
  3073. goto err;
  3074. }
  3075. hr = reobj.poleobj->SetClientSite(reobj.polesite);
  3076. if(hr)
  3077. {
  3078. TRACEERRORSZ("SetClientSite() failed.");
  3079. goto err;
  3080. }
  3081. if(hr = InsertObject(&reobj))
  3082. {
  3083. TRACEERRORSZ("InsertObject() failed.");
  3084. }
  3085. err:
  3086. if(reobj.poleobj)
  3087. reobj.poleobj->Release();
  3088. if(reobj.polesite)
  3089. reobj.polesite->Release();
  3090. if(reobj.pstg)
  3091. reobj.pstg->Release();
  3092. return hr;
  3093. }
  3094. typedef void (WINAPI*DRAGFINISH)(HDROP);
  3095. typedef UINT (WINAPI*DRAGQUERYFILEA)(HDROP, UINT, LPSTR, UINT);
  3096. typedef UINT (WINAPI*DRAGQUERYFILEW)(HDROP, UINT, LPTSTR, UINT);
  3097. typedef BOOL (WINAPI*DRAGQUERYPOINT)(HDROP, LPPOINT);
  3098. LRESULT CTxtEdit::OnDropFiles(
  3099. HANDLE hDropFiles)
  3100. {
  3101. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnDropFiles");
  3102. UINT cFiles;
  3103. UINT iFile;
  3104. char szFile[MAX_PATH];
  3105. WCHAR wFile[MAX_PATH];
  3106. POINT ptDrop;
  3107. CTxtSelection * const psel = GetSel();
  3108. HMODULE hDLL = NULL;
  3109. DRAGFINISH fnDragFinish;
  3110. DRAGQUERYFILEA fnDragQueryFileA;
  3111. DRAGQUERYFILEW fnDragQueryFileW;
  3112. DRAGQUERYPOINT fnDragQueryPoint;
  3113. if (_fReadOnly)
  3114. return 0;
  3115. AssertSz((hDropFiles != NULL), "CTxtEdit::OnDropFiles invalid hDropFiles");
  3116. // dynamic load Shell32
  3117. hDLL = LoadLibrary (TEXT("Shell32.DLL"));
  3118. if(hDLL)
  3119. {
  3120. fnDragFinish = (DRAGFINISH)GetProcAddress (hDLL, "DragFinish");
  3121. fnDragQueryFileA = (DRAGQUERYFILEA)GetProcAddress (hDLL, "DragQueryFileA");
  3122. fnDragQueryFileW = (DRAGQUERYFILEW)GetProcAddress (hDLL, "DragQueryFileW");
  3123. fnDragQueryPoint = (DRAGQUERYPOINT)GetProcAddress (hDLL, "DragQueryPoint");
  3124. }
  3125. else
  3126. return 0;
  3127. if(!fnDragFinish || !fnDragQueryFileA || !fnDragQueryFileW || !fnDragQueryPoint)
  3128. {
  3129. AssertSz(FALSE, "Shell32 GetProcAddress failed");
  3130. goto EXIT0;
  3131. }
  3132. (*fnDragQueryPoint) ((HDROP)hDropFiles, &ptDrop);
  3133. if(W32->OnWin9x())
  3134. cFiles = (*fnDragQueryFileA) ((HDROP)hDropFiles, (UINT)-1, NULL, 0);
  3135. else
  3136. cFiles = (*fnDragQueryFileW) ((HDROP)hDropFiles, (UINT)-1, NULL, 0);
  3137. if(cFiles)
  3138. {
  3139. LONG cp = 0;
  3140. ptDrop;
  3141. CRchTxtPtr rtp(this);
  3142. const CCharFormat *pCF;
  3143. POINTUV pt;
  3144. _pdp->PointuvFromPoint(pt, ptDrop);
  3145. if(_pdp->CpFromPoint(pt, NULL, &rtp, NULL, FALSE) >= 0)
  3146. {
  3147. cp = rtp.GetCp();
  3148. pCF = rtp.GetCF();
  3149. }
  3150. else
  3151. {
  3152. LONG iCF = psel->Get_iCF();
  3153. cp = psel->GetCp();
  3154. pCF = GetCharFormat(iCF);
  3155. ReleaseFormats(iCF, -1);
  3156. }
  3157. // Notify user for dropfile
  3158. if(_dwEventMask & ENM_DROPFILES)
  3159. {
  3160. ENDROPFILES endropfiles;
  3161. endropfiles.hDrop = hDropFiles;
  3162. endropfiles.cp = Get10Mode() ? GetAcpFromCp(cp) : cp;
  3163. endropfiles.fProtected = !!(pCF->_dwEffects & CFE_PROTECTED);
  3164. if(TxNotify(EN_DROPFILES, &endropfiles))
  3165. goto EXIT; // Ignore drop file
  3166. cp = Get10Mode() ? GetCpFromAcp(endropfiles.cp) : endropfiles.cp; // Allow callback to update cp
  3167. }
  3168. psel->SetCp(cp, FALSE);
  3169. }
  3170. for (iFile = 0; iFile < cFiles; iFile++)
  3171. {
  3172. if(W32->OnWin9x())
  3173. {
  3174. (*fnDragQueryFileA) ((HDROP)hDropFiles, iFile, szFile, MAX_PATH);
  3175. MultiByteToWideChar(CP_ACP, 0, szFile, -1,
  3176. wFile, MAX_PATH);
  3177. }
  3178. else
  3179. (*fnDragQueryFileW) ((HDROP)hDropFiles, iFile, wFile, MAX_PATH);
  3180. InsertFromFile (wFile);
  3181. }
  3182. EXIT:
  3183. (*fnDragFinish) ((HDROP)hDropFiles);
  3184. EXIT0:
  3185. FreeLibrary (hDLL);
  3186. return 0;
  3187. }
  3188. #else // NODROPFILES
  3189. LRESULT CTxtEdit::OnDropFiles(HANDLE hDropFiles)
  3190. {
  3191. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnDropFiles");
  3192. return 0;
  3193. }
  3194. #endif // NODROPFILES
  3195. /////////////////////////////// Exposable methods //////////////////////////////////////
  3196. /*
  3197. * CTxtEdit::TxCharFromPos (ppt, plres)
  3198. *
  3199. * @mfunc
  3200. * Get the acp at the point *ppt.
  3201. *
  3202. * @rdesc
  3203. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3204. * (CpFromPoint succeeded) ? S_OK : E_FAIL
  3205. * @comm
  3206. * This function outputs an API cp that may differ from the
  3207. * corresponding internal Unicode cp.
  3208. */
  3209. HRESULT CTxtEdit::TxCharFromPos(
  3210. LPPOINT ppt, //@parm Point to find the acp for
  3211. LRESULT *plres) //@parm Output parm to receive the acp
  3212. {
  3213. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxCharFromPos");
  3214. if(!fInplaceActive())
  3215. {
  3216. // We have no valid display rectangle if this object is not active
  3217. *plres = -1;
  3218. return OLE_E_INVALIDRECT;
  3219. }
  3220. POINTUV pt;
  3221. _pdp->PointuvFromPoint(pt, *ppt);
  3222. *plres = _pdp->CpFromPoint(pt, NULL, NULL, NULL, FALSE);
  3223. if(*plres == -1)
  3224. return E_FAIL;
  3225. *plres = GetAcpFromCp(*plres);
  3226. return S_OK;
  3227. }
  3228. /*
  3229. * CTxtEdit::TxPosFromChar (acp, ppt)
  3230. *
  3231. * @mfunc
  3232. * Get the point at acp.
  3233. *
  3234. * @rdesc
  3235. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3236. * (PointFromTp succeeded) ? S_OK : E_FAIL
  3237. * @comm
  3238. * This function inputs an API cp that may differ from the
  3239. * corresponding internal Unicode cp.
  3240. */
  3241. HRESULT CTxtEdit::TxPosFromChar(
  3242. LONG acp, //@parm Input cp to get the point for
  3243. POINT * ppt) //@parm Output parm to receive the point
  3244. {
  3245. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxPosFromChar");
  3246. if(!fInplaceActive())
  3247. return OLE_E_INVALIDRECT;
  3248. CRchTxtPtr rtp(this, GetCpFromAcp(acp));
  3249. POINTUV pt;
  3250. if(_pdp->PointFromTp(rtp, NULL, FALSE, pt, NULL, TA_TOP) < 0)
  3251. return E_FAIL;
  3252. _pdp->PointFromPointuv(*ppt, pt);
  3253. return S_OK;
  3254. }
  3255. /*
  3256. * CTxtEdit::TxFindWordBreak (nFunction, acp, plres)
  3257. *
  3258. * @mfunc
  3259. * Find word break or classify character at acp.
  3260. *
  3261. * @rdesc
  3262. * HRESULT = plRet ? S_OK : E_INVALIDARG
  3263. *
  3264. * @comm
  3265. * This function inputs and exports API cp's and cch's that may differ
  3266. * from the internal Unicode cp's and cch's.
  3267. */
  3268. HRESULT CTxtEdit::TxFindWordBreak(
  3269. INT nFunction, //@parm Word break function
  3270. LONG acp, //@parm Input cp
  3271. LRESULT *plres) //@parm cch moved to reach break
  3272. {
  3273. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxFindWordBreak");
  3274. CTxtPtr tp(this, GetCpFromAcp(acp)); // This validates cp
  3275. LONG cpSave = tp.GetCp(); // Save starting value
  3276. if(!plres)
  3277. return E_INVALIDARG;
  3278. *plres = tp.FindWordBreak(nFunction);
  3279. // WB_CLASSIFY and WB_ISDELIMITER return values; others return offsets
  3280. // this function returns values, so it converts when necessary
  3281. if(nFunction != WB_CLASSIFY && nFunction != WB_ISDELIMITER)
  3282. *plres = GetAcpFromCp(LONG(*plres + cpSave));
  3283. return S_OK;
  3284. }
  3285. /*
  3286. * INT CTxtEdit::TxWordBreakProc (pch, ich, cb, action)
  3287. *
  3288. * @func
  3289. * Default word break proc used in conjunction with FindWordBreak. ich
  3290. * is character offset (start position) in the buffer pch, which is cb
  3291. * bytes in length. Possible action values are:
  3292. *
  3293. * WB_CLASSIFY
  3294. * Returns char class and word break flags of char at start position.
  3295. *
  3296. * WB_ISDELIMITER
  3297. * Returns TRUE iff char at start position is a delimeter.
  3298. *
  3299. * WB_LEFT
  3300. * Finds nearest word beginning before start position using word breaks.
  3301. *
  3302. * WB_LEFTBREAK
  3303. * Finds nearest word end before start position using word breaks.
  3304. * Used by CMeasurer::Measure()
  3305. *
  3306. * WB_MOVEWORDLEFT
  3307. * Finds nearest word beginning before start position using class
  3308. * differences. This value is used during CTRL+LEFT key processing.
  3309. *
  3310. * WB_MOVEWORDRIGHT
  3311. * Finds nearest word beginning after start position using class
  3312. * differences. This value is used during CTRL+RIGHT key processing.
  3313. *
  3314. * WB_RIGHT
  3315. * Finds nearest word beginning after start position using word breaks.
  3316. * Used by CMeasurer::Measure()
  3317. *
  3318. * WB_RIGHTBREAK
  3319. * Finds nearest word end after start position using word breaks.
  3320. *
  3321. * @rdesc
  3322. * Character offset from start of buffer (pch) of the word break
  3323. */
  3324. INT CTxtEdit::TxWordBreakProc(
  3325. WCHAR * pch, //@parm Char buffer
  3326. INT ich, //@parm Char offset of _cp in buffer
  3327. INT cb, //@parm Count of bytes in buffer
  3328. INT action, //@parm Type of breaking action
  3329. LONG cpStart, //@parm cp for first character in pch
  3330. LONG cp) //@parm cp associated to ich
  3331. {
  3332. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxWordBreakProc");
  3333. if (_pfnWB)
  3334. {
  3335. // Client overrode the wordbreak proc, delegate the call to it.
  3336. if (!Get10Mode())
  3337. {
  3338. Assert(!_fExWordBreakProc);
  3339. //8638: return number of characters, not bytes.
  3340. return _pfnWB(pch, ich, CchOfCb(cb), action);
  3341. }
  3342. #ifndef NOANSIWINDOWS
  3343. else
  3344. {
  3345. int ret = 0;
  3346. char sz[256];
  3347. char* pach = sz;
  3348. if (cb >= 255)
  3349. pach = new char [cb + 1];
  3350. // this indicates if we have to adjust the pach because the api's for
  3351. // EDITWORDBREAKPROCEX and EDITWORDBREAKPROC are different when looking to the left
  3352. BOOL fAdjustPtr = _fExWordBreakProc && (action == WB_LEFT || action == WB_MOVEWORDLEFT || action == WB_LEFTBREAK);
  3353. // RichEdit 1.0, create a buffer, translate ich and WCTMB
  3354. // pch into the buffer. Need codepage to use. Then get translate
  3355. // return value. Translations are like GetCachFromCch() and
  3356. // GetCchFromCach()
  3357. if (_fExWordBreakProc)
  3358. {
  3359. Assert(ich == 0 || ich == 1 || ich == CchOfCb(cb));
  3360. // We need to adjust the cp to the starting point of the buffer
  3361. if (!fAdjustPtr)
  3362. {
  3363. cpStart += ich;
  3364. pch += ich;
  3365. cb -= (2 * ich);
  3366. }
  3367. // initialize string w/ zero's so we can determine the length of the string for later
  3368. memset(pach, 0, cb + 1);
  3369. }
  3370. int nLen = CchOfCb(cb);
  3371. CRchTxtPtr rtp(this, cpStart);
  3372. BYTE iCharRep = rtp.GetCF()->_iCharRep;
  3373. if (WideCharToMultiByte(CodePageFromCharRep(iCharRep), 0, pch, nLen, pach, cb + 1, NULL, NULL))
  3374. {
  3375. // Documentation stipulates we need to point to the end of the string
  3376. if (fAdjustPtr)
  3377. pach += strlen(pach);
  3378. if (_fExWordBreakProc)
  3379. ret = ((EDITWORDBREAKPROCEX)_pfnWB)(pach, nLen, CharSetFromCharRep(iCharRep), action);
  3380. else
  3381. {
  3382. ret = ((EDITWORDBREAKPROCA)_pfnWB)(pach, rtp.GetCachFromCch(ich), nLen, action);
  3383. // Need to reset cp position because GetCachFromCch may move the cp
  3384. if (ich)
  3385. rtp.SetCp(cpStart);
  3386. }
  3387. // For WB_ISDELIMITER and WB_CLASSIFY don't need to convert back
  3388. // to ich because return value represents a BOOL
  3389. if (action != WB_ISDELIMITER && action != WB_CLASSIFY)
  3390. ret = rtp.GetCchFromCach(ret);
  3391. }
  3392. // Delete any allocated memory
  3393. if (pach != sz)
  3394. delete [] pach;
  3395. return ret;
  3396. }
  3397. #endif // NOANSIWINDOWS
  3398. }
  3399. LONG cchBuff = CchOfCb(cb);
  3400. LONG cch = cchBuff - ich;
  3401. WCHAR ch;
  3402. WORD cType3[MAX_CLASSIFY_CHARS];
  3403. INT kinsokuClassifications[MAX_CLASSIFY_CHARS];
  3404. LCID lcid = 0;
  3405. WORD * pcType3;
  3406. INT * pKinsoku1, *pKinsoku2;
  3407. WORD * pwRes;
  3408. WORD startType3 = 0;
  3409. WORD wb = 0;
  3410. WORD wClassifyData[MAX_CLASSIFY_CHARS]; // For batch classifying
  3411. Assert(cchBuff < MAX_CLASSIFY_CHARS);
  3412. Assert(ich >= 0 && ich < cchBuff);
  3413. if(W32->OnWin9x()) // Win9x needs lcid to do conversions
  3414. { // Complete fix would break pch into
  3415. CFormatRunPtr rpCF(_story.GetCFRuns());// runs <--> lcid
  3416. rpCF.BindToCp(cp, GetTextLength());
  3417. lcid = GetCharFormat(rpCF.GetFormat())->_lcid;
  3418. }
  3419. // Single character actions
  3420. if ( action == WB_CLASSIFY )
  3421. {
  3422. // 1.0 COMPATABILITY - 1.0 returned 0 for apostrohpe's
  3423. WCHAR ch = pch[ich];
  3424. if (Get10Mode() && ( ch == 0x0027 /*APOSTROPHE*/ ||
  3425. ch == 0xFF07 /*FULLWIDTH APOSTROPHE*/))
  3426. {
  3427. return 0;
  3428. }
  3429. return ClassifyChar(ch, lcid);
  3430. }
  3431. if ( action == WB_ISDELIMITER )
  3432. return !!(ClassifyChar(pch[ich], lcid) & WBF_BREAKLINE);
  3433. // Batch classify buffer for whitespace and kinsoku classes
  3434. BatchClassify(pch, cchBuff, lcid, cType3, kinsokuClassifications, wClassifyData);
  3435. #ifndef NOCOMPLEXSCRIPTS
  3436. if (_pbrk && cp > -1)
  3437. {
  3438. cp -= ich;
  3439. for (LONG cbrk = cchBuff-1; cbrk >= 0; --cbrk)
  3440. {
  3441. if (cp + cbrk >= 0 && _pbrk->CanBreakCp(BRK_WORD, cp + cbrk))
  3442. {
  3443. // Mimic class open/close in Kinsoku classification.
  3444. kinsokuClassifications[cbrk] = brkclsOpen;
  3445. if (cbrk > 0)
  3446. {
  3447. kinsokuClassifications[cbrk-1] = brkclsClose;
  3448. wClassifyData[cbrk-1] |= WBF_WORDBREAKAFTER;
  3449. }
  3450. }
  3451. }
  3452. }
  3453. #endif
  3454. // Setup pointers
  3455. pKinsoku2 = kinsokuClassifications + ich; // Ptr to current kinsoku
  3456. pKinsoku1 = pKinsoku2 - 1; // Ptr to previous kinsoku
  3457. if(!(action & 1)) // WB_(MOVE)LEFTxxx
  3458. {
  3459. ich--;
  3460. Assert(ich >= 0);
  3461. }
  3462. pwRes = &wClassifyData[ich];
  3463. pcType3 = &cType3[ich]; // for ideographics
  3464. switch(action)
  3465. {
  3466. case WB_LEFT:
  3467. for(; ich >= 0 && *pwRes & WBF_BREAKLINE; // Skip preceding line
  3468. ich--, pwRes--) // break chars
  3469. ; // Empty loop. Then fall
  3470. // thru to WB_LEFTBREAK
  3471. case WB_LEFTBREAK:
  3472. for(; ich >= 0 && !CanBreak(*pKinsoku1, *pKinsoku2);
  3473. ich--, pwRes--, pKinsoku1--, pKinsoku2--)
  3474. ; // Empty loop
  3475. if(action == WB_LEFTBREAK) // Skip preceding line
  3476. { // break chars
  3477. for(; ich >= 0 && *pwRes & WBF_BREAKLINE;
  3478. ich--, pwRes--)
  3479. ; // Empty loop
  3480. }
  3481. return ich + 1;
  3482. case WB_MOVEWORDLEFT:
  3483. for(; ich >= 0 && (*pwRes & WBF_CLASS) == 2;// Skip preceding blank
  3484. ich--, pwRes--, pcType3--) // chars
  3485. ;
  3486. if(ich >= 0) // Save starting wRes and
  3487. { // startType3
  3488. wb = *pwRes--; // Really type1
  3489. startType3 = *pcType3--; // type3
  3490. ich--;
  3491. }
  3492. // Skip to beginning of current word
  3493. while(ich >= 0 && (*pwRes & WBF_CLASS) != 3 &&
  3494. !(*pwRes & WBF_WORDBREAKAFTER) &&
  3495. (IsSameClass(*pwRes, wb, *pcType3, startType3) ||
  3496. !wb && ich && ((ch = pch[ich]) == '\'' || ch == RQUOTE)))
  3497. {
  3498. ich--, pwRes--, pcType3--;
  3499. }
  3500. return ich + 1;
  3501. case WB_RIGHTBREAK:
  3502. for(; cch > 0 && *pwRes & WBF_BREAKLINE; // Skip any leading line
  3503. cch--, pwRes++) // break chars
  3504. ; // Empty loop
  3505. // Fall thru to WB_RIGHT
  3506. case WB_RIGHT:
  3507. // Skip to end of current word
  3508. for(; cch > 0 && !CanBreak(*pKinsoku1, *pKinsoku2);
  3509. cch--, pKinsoku1++, pKinsoku2++, pwRes++)
  3510. ;
  3511. if(action != WB_RIGHTBREAK) // Skip trailing line
  3512. { // break chars
  3513. for(; cch > 0 && *pwRes & WBF_BREAKLINE;
  3514. cch--, pwRes++)
  3515. ;
  3516. }
  3517. return cchBuff - cch;
  3518. case WB_MOVEWORDRIGHT:
  3519. if(cch <= 0) // Nothing to do
  3520. return ich;
  3521. wb = *pwRes; // Save start wRes
  3522. startType3 = *pcType3; // and startType3
  3523. // Skip to end of word
  3524. if (startType3 & C3_IDEOGRAPH || // If ideographic or
  3525. (*pwRes & WBF_CLASS) == 3) // tab/cell, just
  3526. {
  3527. cch--, pwRes++; // skip one char
  3528. }
  3529. else while(cch > 0 &&
  3530. !(*pwRes & WBF_WORDBREAKAFTER) &&
  3531. (IsSameClass(*pwRes, wb, *pcType3, startType3) || !wb &&
  3532. ((ch = pch[cchBuff - cch]) == '\'' || ch == RQUOTE)))
  3533. {
  3534. cch--, pwRes++, pcType3++;
  3535. }
  3536. for(; cch > 0 &&
  3537. ((*pwRes & WBF_CLASS) == 2 // Skip trailing blank
  3538. || (*pwRes & WBF_WORDBREAKAFTER)); // Skip Thai break after
  3539. cch--, pwRes++) // chars
  3540. ;
  3541. return cchBuff - cch;
  3542. }
  3543. TRACEERRSZSC("CTxtEdit::TxWordBreakProc: unknown action", action);
  3544. return ich;
  3545. }
  3546. /*
  3547. * CTxtEdit::TxFindText (flags, cpMin, cpMost, pch, pcpRet)
  3548. *
  3549. * @mfunc
  3550. * Find text in direction specified by flags starting at cpMin if
  3551. * forward search (flags & FR_DOWN nonzero) and cpMost if backward
  3552. * search.
  3553. *
  3554. * @rdesc
  3555. * HRESULT (success) ? NOERROR : S_FALSE
  3556. *
  3557. * @comm
  3558. * Caller is responsible for setting cpMin to the appropriate end of
  3559. * the selection depending on which way the search is proceding.
  3560. */
  3561. HRESULT CTxtEdit::TxFindText(
  3562. DWORD flags, //@parm Specify FR_DOWN, FR_MATCHCASE, FR_WHOLEWORD
  3563. LONG cpStart, //@parm Find start cp
  3564. LONG cpLimit, //@parm Find limit cp
  3565. const WCHAR*pch, //@parm Null terminated string to search for
  3566. LONG * pcpMin, //@parm Out parm to receive start of matched string
  3567. LONG * pcpMost) //@parm Out parm to receive end of matched string
  3568. {
  3569. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxFindText");
  3570. if(Get10Mode()) // RichEdit 1.0 only searches
  3571. {
  3572. flags |= FR_DOWN; // forward
  3573. if (cpLimit < -1)
  3574. cpLimit = -1;
  3575. }
  3576. DWORD cchText = GetTextLength();
  3577. LONG cchToFind;
  3578. const BOOL fSetCur = (cchText >= 4096);
  3579. HCURSOR hcur = NULL; // Init to keep compiler happy
  3580. Assert(pcpMin && pcpMost);
  3581. // Validate parameters
  3582. if(!pch || !(cchToFind = wcslen(pch)) || cpStart < 0 || cpLimit < -1)
  3583. return E_INVALIDARG; // Nothing to search for
  3584. CTxtPtr tp(this, cpStart);
  3585. if(fSetCur) // In case this takes a while...
  3586. hcur = TxSetCursor(LoadCursor(0, IDC_WAIT), NULL);
  3587. *pcpMin = tp.FindText(cpLimit, flags, pch, cchToFind);
  3588. *pcpMost = tp.GetCp();
  3589. if(fSetCur)
  3590. TxSetCursor(hcur, NULL);
  3591. return *pcpMin >= 0 ? NOERROR : S_FALSE;
  3592. }
  3593. /*
  3594. * CTxtEdit::TxGetLineCount (plres)
  3595. *
  3596. * @mfunc
  3597. * Get the line count.
  3598. *
  3599. * @rdesc
  3600. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3601. * (WaitForRecalc succeeded) ? S_OK : E_FAIL
  3602. */
  3603. HRESULT CTxtEdit::TxGetLineCount(
  3604. LRESULT *plres) //@parm Output parm to receive line count
  3605. {
  3606. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetLineCount");
  3607. AssertSz(plres, "CTxtEdit::TxGetLineCount invalid pcli");
  3608. if(!fInplaceActive())
  3609. return OLE_E_INVALIDRECT;
  3610. if(!_pdp->WaitForRecalc(GetTextLength(), -1))
  3611. return E_FAIL;
  3612. *plres = _pdp->LineCount();
  3613. Assert(*plres > 0);
  3614. return S_OK;
  3615. }
  3616. /*
  3617. * CTxtEdit::TxLineFromCp (acp, plres)
  3618. *
  3619. * @mfunc
  3620. * Get the line containing acp.
  3621. *
  3622. * @rdesc
  3623. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3624. * (LineFromCp succeeded) ? S_OK : E_FAIL
  3625. * @comm
  3626. * This function inputs an API cp that may differ from the
  3627. * corresponding internal Unicode cp.
  3628. */
  3629. HRESULT CTxtEdit::TxLineFromCp(
  3630. LONG acp, //@parm Input cp
  3631. LRESULT *plres) //@parm Ouput parm to receive line number
  3632. {
  3633. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxLineFromCp");
  3634. BOOL fAtEnd = FALSE;
  3635. LONG cp = 0;
  3636. AssertSz(plres, "CTxtEdit::TxLineFromCp invalid plres");
  3637. if(!fInplaceActive())
  3638. {
  3639. AssertSz(*plres == 0,
  3640. "CTxtEdit::TxLineFromCp error return lres not correct");
  3641. return OLE_E_INVALIDRECT;
  3642. }
  3643. if(acp < 0) // Validate cp
  3644. {
  3645. if(_psel)
  3646. {
  3647. cp = _psel->GetCpMin();
  3648. fAtEnd = !_psel->GetCch() && _psel->CaretNotAtBOL();
  3649. }
  3650. }
  3651. else
  3652. {
  3653. LONG cchText = GetTextLength();
  3654. cp = GetCpFromAcp(acp);
  3655. cp = min(cp, cchText);
  3656. }
  3657. *plres = _pdp->LineFromCp(cp, fAtEnd);
  3658. HRESULT hr = *plres < 0 ? E_FAIL : S_OK;
  3659. // Old messages expect 0 as a result of this call if there is an error.
  3660. if(*plres == -1)
  3661. *plres = 0;
  3662. return hr;
  3663. }
  3664. /*
  3665. * CTxtEdit::TxLineLength (acp, plres)
  3666. *
  3667. * @mfunc
  3668. * Get the line containing acp.
  3669. *
  3670. * @rdesc
  3671. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3672. * (GetSel() succeeded) ? S_OK : E_FAIL
  3673. * @comm
  3674. * This function inputs an API cp and outputs an API cch that
  3675. * may differ from the corresponding internal Unicode cp and cch.
  3676. */
  3677. HRESULT CTxtEdit::TxLineLength(
  3678. LONG acp, //@parm Input cp
  3679. LRESULT *plres) //@parm Output parm to receive line length
  3680. {
  3681. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxLineLength");
  3682. LONG cch = 0;
  3683. LONG cp;
  3684. AssertSz(plres, "CTxtEdit::TxLineLength Invalid plres parameter");
  3685. if(!fInplaceActive())
  3686. return OLE_E_INVALIDRECT;
  3687. if(acp < 0)
  3688. {
  3689. if(!_psel)
  3690. return E_FAIL;
  3691. cch = _psel->LineLength(&cp);
  3692. }
  3693. else
  3694. {
  3695. cp = GetCpFromAcp(acp);
  3696. if(cp <= GetAdjustedTextLength())
  3697. {
  3698. CLinePtr rp(_pdp);
  3699. rp.SetCp(cp, FALSE);
  3700. cp -= rp.GetIch(); // Goto start of line
  3701. cch = rp.GetAdjustedLineLength();
  3702. }
  3703. }
  3704. if(fCpMap()) // Can be time consuming, so
  3705. { // don't do it unless asked
  3706. CRchTxtPtr rtp(this, cp); // for
  3707. cch = rtp.GetCachFromCch(cch);
  3708. }
  3709. *plres = cch;
  3710. return S_OK;
  3711. }
  3712. /*
  3713. * CTxtEdit::TxLineIndex (acp, plres)
  3714. *
  3715. * @mfunc
  3716. * Get the line containing acp.
  3717. *
  3718. * @rdesc
  3719. * HRESULT = !fInplaceActive() ? OLE_E_INVALIDRECTS_OK :
  3720. * (LineCount() && WaitForRecalcIli succeeded) ? S_OK : E_FAIL
  3721. * @comm
  3722. * This function outputs an API cp that may differ from the
  3723. * corresponding internal Unicode cp.
  3724. */
  3725. HRESULT CTxtEdit::TxLineIndex(
  3726. LONG ili, //@parm Line # to find acp for
  3727. LRESULT *plres) //@parm Output parm to receive acp
  3728. {
  3729. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxLineIndex");
  3730. HRESULT hr;
  3731. AssertSz(plres, "CTxtEdit::TxLineIndex invalid plres");
  3732. *plres = -1;
  3733. if(!fInplaceActive())
  3734. return OLE_E_INVALIDRECT;
  3735. if(ili == -1)
  3736. {
  3737. // Fetch line from the current cp.
  3738. LRESULT lres; // For 64-bit compatibility
  3739. hr = TxLineFromCp(-1, &lres);
  3740. if(hr != NOERROR)
  3741. return hr;
  3742. ili = (LONG)lres;
  3743. }
  3744. // ili is a zero-based *index*, whereas count returns the total # of lines.
  3745. // Therefore, we use >= for our comparisions.
  3746. if(ili >= _pdp->LineCount() && !_pdp->WaitForRecalcIli(ili))
  3747. return E_FAIL;
  3748. *plres = GetAcpFromCp(_pdp->CpFromLine(ili, NULL));
  3749. return S_OK;
  3750. }
  3751. /////////////////////////////////// Miscellaneous messages ////////////////////////////////////
  3752. /*
  3753. * CTxtEdit::OnFindText (msg, flags, pftex)
  3754. *
  3755. * @mfunc
  3756. * Find text.
  3757. *
  3758. * @rdesc
  3759. * LRESULT = succeeded ? acpmin : -1
  3760. *
  3761. * @comm
  3762. * This function inputs and exports API cp's that may differ
  3763. * from the internal Unicode cp's.
  3764. */
  3765. LRESULT CTxtEdit::OnFindText(
  3766. UINT msg,
  3767. DWORD flags,
  3768. FINDTEXTEX *pftex)
  3769. {
  3770. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnFindText");
  3771. LONG cpMin, cpMost;
  3772. if(TxFindText(flags,
  3773. GetCpFromAcp(pftex->chrg.cpMin),
  3774. GetCpFromAcp(pftex->chrg.cpMost),
  3775. pftex->lpstrText, &cpMin, &cpMost) != S_OK)
  3776. {
  3777. if(msg == EM_FINDTEXTEX || msg == EM_FINDTEXTEXW)
  3778. {
  3779. pftex->chrgText.cpMin = -1;
  3780. pftex->chrgText.cpMost = -1;
  3781. }
  3782. return -1;
  3783. }
  3784. LONG acpMin = GetAcpFromCp(cpMin);
  3785. if(msg == EM_FINDTEXTEX || msg == EM_FINDTEXTEXW) // We send a message
  3786. { // back to change
  3787. pftex->chrgText.cpMin = acpMin; // selection to this
  3788. pftex->chrgText.cpMost = GetAcpFromCp(cpMost);
  3789. }
  3790. return (LRESULT)acpMin;
  3791. }
  3792. // For plain-text instances, OnGetParaFormat() and OnSetParaFormat() apply to whole story
  3793. LRESULT CTxtEdit::OnGetCharFormat(
  3794. CHARFORMAT2 *pCF2,
  3795. DWORD dwFlags)
  3796. {
  3797. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnGetCharFormat");
  3798. UINT cb = pCF2->cbSize;
  3799. UINT CodePage = 1200;
  3800. if(!IsValidCharFormatW(pCF2))
  3801. {
  3802. if(!IsValidCharFormatA((CHARFORMATA *)pCF2))
  3803. return 0;
  3804. CodePage = GetDefaultCodePage(EM_GETCHARFORMAT);
  3805. }
  3806. if(dwFlags & (SCF_ASSOCIATEFONT | SCF_ASSOCIATEFONT2))
  3807. return OnGetAssociateFont(pCF2, dwFlags);
  3808. if(cb == sizeof(CHARFORMATW) || cb == sizeof(CHARFORMATA))
  3809. dwFlags |= CFM2_CHARFORMAT; // Tell callees that only
  3810. // CHARFORMAT parms needed
  3811. CCharFormat CF;
  3812. DWORD dwMask = CFM_ALL2;
  3813. if(dwFlags & SCF_SELECTION)
  3814. dwMask = GetSel()->GetCharFormat(&CF, dwFlags);
  3815. else
  3816. CF = *GetCharFormat(-1);
  3817. if(dwFlags & CFM2_CHARFORMAT) // Maintain CHARFORMAT
  3818. { // compatibility
  3819. CF._dwEffects &= CFM_EFFECTS;
  3820. dwMask &= CFM_ALL;
  3821. }
  3822. CF.Get(pCF2, CodePage);
  3823. pCF2->dwMask = dwMask;
  3824. return (LRESULT)dwMask;
  3825. }
  3826. LRESULT CTxtEdit::OnGetParaFormat(
  3827. PARAFORMAT2 *pPF2,
  3828. DWORD dwFlags)
  3829. {
  3830. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnGetParaFormat");
  3831. if(!IsValidParaFormat(pPF2))
  3832. return 0;
  3833. DWORD dwMask2 = 0;
  3834. if(pPF2->cbSize == sizeof(PARAFORMAT)) // Tell callees that only
  3835. dwMask2 = PFM2_PARAFORMAT; // PARAFORMAT parms needed
  3836. CParaFormat PF;
  3837. DWORD dwMask = GetSel()->GetParaFormat(&PF, dwMask2);
  3838. if(dwMask2 & PFM2_PARAFORMAT)
  3839. dwMask &= PFM_ALL;
  3840. PF.Get(pPF2);
  3841. pPF2->dwMask = dwMask;
  3842. return (LRESULT)dwMask;
  3843. }
  3844. /*
  3845. * CTxtEdit::OnSetFontSize(yPoint, publdr)
  3846. *
  3847. * @mfunc
  3848. * Set new font height by adding yPoint to current height
  3849. * and rounding according to the table in cfpf.cpp
  3850. *
  3851. * @rdesc
  3852. * LRESULT nonzero if success
  3853. */
  3854. LRESULT CTxtEdit::OnSetFontSize(
  3855. LONG yPoint, //@parm # pts to add to current height
  3856. DWORD dwFlags, //@parm Options
  3857. IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
  3858. {
  3859. // TODO: ? Return nonzero if we set a new font size for some text.
  3860. CCharFormat CF;
  3861. CF._yHeight = (SHORT)yPoint;
  3862. return OnSetCharFormat(dwFlags ? dwFlags : SCF_SELECTION, &CF, publdr,
  3863. CFM_SIZE, CFM2_CHARFORMAT | CFM2_USABLEFONT);
  3864. }
  3865. /*
  3866. * CTxtEdit::OnSetFont(hfont)
  3867. *
  3868. * @mfunc
  3869. * Set new default font from hfont
  3870. *
  3871. * @rdesc
  3872. * LRESULT nonzero if success
  3873. */
  3874. LRESULT CTxtEdit::OnSetFont(
  3875. HFONT hfont) //@parm Handle of font to use for default
  3876. {
  3877. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetFont");
  3878. CCharFormat CF;
  3879. if(FAILED(CF.InitDefault(hfont)))
  3880. return 0;
  3881. DWORD dwMask2 = CFM2_CHARFORMAT;
  3882. WPARAM wparam = SCF_ALL;
  3883. if(!GetAdjustedTextLength())
  3884. {
  3885. dwMask2 = CFM2_CHARFORMAT | CFM2_NOCHARSETCHECK;
  3886. wparam = 0;
  3887. }
  3888. return !FAILED(OnSetCharFormat(wparam, &CF, NULL, CFM_ALL, dwMask2));
  3889. }
  3890. /*
  3891. * CTxtEdit::OnSetCharFormat(wparam, pCF, publdr, dwMask, dwMask2)
  3892. *
  3893. * @mfunc
  3894. * Set new default CCharFormat
  3895. *
  3896. * @rdesc
  3897. * LRESULT nonzero if success
  3898. */
  3899. LRESULT CTxtEdit::OnSetCharFormat(
  3900. WPARAM wparam, //@parm Selection flag
  3901. CCharFormat * pCF, //@parm CCharFormat to apply
  3902. IUndoBuilder *publdr, //@parm Undobuilder to receive antievents
  3903. DWORD dwMask, //@parm CHARFORMAT2 mask
  3904. DWORD dwMask2) //@parm Second mask
  3905. {
  3906. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetCharFormat");
  3907. // This says that if there's a selection that's protected and the
  3908. // parent window wants protection notifications and doesn't want
  3909. // changes with a protected selection, then return 0. This is more
  3910. // stringent than RE 2.0, but it's more like 1.0.
  3911. if (_psel && _psel->IsProtected(CHKPROT_EITHER) == PROTECTED_ASK &&
  3912. _dwEventMask & ENM_PROTECTED)
  3913. {
  3914. CHARFORMAT CF0; // Selection is protected, client
  3915. // wants protect notifications
  3916. CF0.cbSize = sizeof(CHARFORMAT);// and protected mask is on
  3917. CF0.dwEffects = pCF->_dwEffects;// Concoct CHARFORMAT for query
  3918. CF0.dwMask = dwMask; // Maybe need more fields...
  3919. if(QueryUseProtection(_psel, EM_SETCHARFORMAT, wparam, (LPARAM)&CF0))
  3920. return 0; // No deal
  3921. }
  3922. BOOL fRet = TRUE;
  3923. AssertSz(!_fSelChangeCharFormat || IsRich(),
  3924. "Inconsistent _fSelChangeCharFormat flag");
  3925. if ((wparam & SCF_ALL) ||
  3926. !_fSelChangeCharFormat && _story.GetCFRuns() && !(wparam & SCF_SELECTION))
  3927. {
  3928. CTxtRange rg(this, 0, -GetTextLength());
  3929. if(publdr)
  3930. publdr->StopGroupTyping();
  3931. if ((dwMask & (CFM_CHARSET | CFM_FACE)) == (CFM_CHARSET | CFM_FACE))
  3932. {
  3933. if(GetAdjustedTextLength())
  3934. {
  3935. dwMask2 |= CFM2_MATCHFONT;
  3936. if (_fAutoFontSizeAdjust)
  3937. {
  3938. dwMask2 |= CFM2_ADJUSTFONTSIZE;
  3939. if (fUseUIFont())
  3940. dwMask2 |= CFM2_UIFONT;
  3941. }
  3942. }
  3943. else
  3944. dwMask2 |= CFM2_NOCHARSETCHECK;
  3945. }
  3946. fRet = (rg.SetCharFormat(pCF, 0, publdr, dwMask, dwMask2) == NOERROR);
  3947. // If we have an insertion point, apply format to it as well
  3948. if (_psel && !_psel->GetCch() &&
  3949. _psel->SetCharFormat(pCF, wparam, publdr, dwMask, dwMask2) != NOERROR)
  3950. {
  3951. fRet = FALSE;
  3952. }
  3953. }
  3954. else if(wparam & SCF_SELECTION)
  3955. {
  3956. // Change selection character format unless protected
  3957. if(!_psel || !IsRich())
  3958. return 0;
  3959. return _psel->SetCharFormat(pCF, wparam, publdr, dwMask, dwMask2)
  3960. == NOERROR;
  3961. }
  3962. // Change default character format
  3963. CCharFormat CF; // Local CF to party on
  3964. LONG iCF; // Possible new CF index
  3965. const CCharFormat *pCF1; // Ptr to current default CF
  3966. ICharFormatCache *pICFCache = GetCharFormatCache();
  3967. if(FAILED(pICFCache->Deref(Get_iCF(), &pCF1))) // Get ptr to current
  3968. { // default CCharFormat
  3969. fRet = FALSE;
  3970. goto Update;
  3971. }
  3972. CF = *pCF1; // Copy current default CF
  3973. CF.Apply(pCF, dwMask, dwMask2); // Modify copy
  3974. if(FAILED(pICFCache->Cache(&CF, &iCF))) // Cache modified copy
  3975. {
  3976. fRet = FALSE;
  3977. goto Update;
  3978. }
  3979. #ifndef NOLINESERVICES
  3980. if (g_pols)
  3981. g_pols->DestroyLine(NULL);
  3982. #endif
  3983. pICFCache->Release(Get_iCF()); // Release _iCF regardless
  3984. Set_iCF(iCF); // of whether _iCF = iCF,
  3985. // i.e., only 1 ref count
  3986. if(_psel && !_psel->GetCch() && _psel->Get_iFormat() == -1)
  3987. _psel->UpdateCaret(FALSE);
  3988. if ((dwMask & (CFM_CHARSET | CFM_FACE)) == CFM_FACE &&
  3989. !GetFontName(pCF->_iFont)[0] && GetFontName(CF._iFont)[0] &&
  3990. IsBiDiCharRep(CF._iCharRep))
  3991. {
  3992. // Client requested font/charset be chosen for it according to thread
  3993. // locale. If BiDi, then also set RTL para default
  3994. CParaFormat PF;
  3995. PF._wEffects = PFE_RTLPARA;
  3996. OnSetParaFormat(SPF_SETDEFAULT, &PF, publdr, PFM_RTLPARA, PFM2_PARAFORMAT);
  3997. }
  3998. Update:
  3999. // FUTURE (alexgo): this may be unnecessary if the display handles
  4000. // updating more automatically.
  4001. _pdp->UpdateView();
  4002. return fRet;
  4003. }
  4004. /*
  4005. * CTxtEdit::OnSetParaFormat(wparam, pPF, publdr, dwMask, dwMask2)
  4006. *
  4007. * @mfunc
  4008. * Set new default CParaFormat
  4009. *
  4010. * @rdesc
  4011. * LRESULT nonzero if success
  4012. */
  4013. LRESULT CTxtEdit::OnSetParaFormat(
  4014. WPARAM wparam, //@parm wparam passed thru to IsProtected()
  4015. CParaFormat *pPF, //@parm CParaFormat to use
  4016. IUndoBuilder *publdr, //@parm Undobuilder to receive antievents
  4017. DWORD dwMask, //@parm CHARFORMAT2 mask
  4018. DWORD dwMask2) //@parm Second mask
  4019. {
  4020. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetParaFormat");
  4021. // If we're using context direction in the control, then we disallow
  4022. // the paragraph direction property and the alignment property (unless
  4023. // it's for center alignment).
  4024. if(IsStrongContext(_nContextDir) || IsStrongContext(_nContextAlign))
  4025. {
  4026. Assert(!IsRich());
  4027. if(dwMask & (PFM_RTLPARA | PFM_ALIGNMENT))
  4028. {
  4029. if (IsStrongContext(_nContextAlign) &&
  4030. (pPF->_bAlignment == PFA_LEFT || pPF->_bAlignment == PFA_RIGHT))
  4031. {
  4032. dwMask &= ~PFM_ALIGNMENT;
  4033. }
  4034. if(IsStrongContext(_nContextDir))
  4035. dwMask &= ~PFM_RTLPARA;
  4036. }
  4037. }
  4038. BOOL fMatchKbdToPara = FALSE;
  4039. if(dwMask & PFM_RTLPARA)
  4040. {
  4041. // In plain text allow DIR changes to change DIR and ALIGNMENT
  4042. if(!IsRich())
  4043. {
  4044. // Clear all para masks, except for DIR and ALIGN
  4045. dwMask &= (PFM_RTLPARA | PFM_ALIGNMENT);
  4046. wparam |= SPF_SETDEFAULT;
  4047. }
  4048. if(_psel && _fFocus)
  4049. fMatchKbdToPara = TRUE;
  4050. }
  4051. if(!(wparam & SPF_SETDEFAULT))
  4052. {
  4053. // If DEFAULT flag is specified, don't change selection
  4054. if(!_psel || IsProtected(EM_SETPARAFORMAT, wparam, (LPARAM)pPF))
  4055. return 0;
  4056. LRESULT lres = NOERROR == (pPF->fSetStyle(dwMask, dwMask2)
  4057. ? _psel->SetParaStyle(pPF, publdr, dwMask)
  4058. : _psel->SetParaFormat(pPF, publdr, dwMask, dwMask2));
  4059. // This is a bit funky, but basically, if the text is empty
  4060. // then we also need to set the default paragraph format
  4061. // (done in the code below). Thus, if we hit a failure or
  4062. // if the document is not empty, go ahead and return.
  4063. // Otherwise, fall through to the default case.
  4064. if(!lres || GetAdjustedTextLength())
  4065. {
  4066. if(fMatchKbdToPara)
  4067. _psel->MatchKeyboardToPara();
  4068. return lres;
  4069. }
  4070. }
  4071. // No text in document or (wparam & SPF_SETDEFAULT): set default format
  4072. LONG iPF; // Possible new PF index
  4073. CParaFormat PF = *GetParaFormat(-1); // Local PF to party on
  4074. IParaFormatCache *pPFCache = GetParaFormatCache();
  4075. PF.Apply(pPF, dwMask, dwMask2); // Modify copy
  4076. if(FAILED(pPFCache->Cache(&PF, &iPF))) // Cache modified copy
  4077. return 0;
  4078. pPFCache->Release(Get_iPF()); // Release _iPF regardless of
  4079. Set_iPF(iPF); // Update default format index
  4080. if(PF.IsRtlPara())
  4081. OrCharFlags(FRTL, publdr); // BiDi in backing store
  4082. if(!IsRich() && dwMask & PFM_RTLPARA) // Changing plain-text default PF
  4083. {
  4084. ItemizeDoc(publdr); // causing re-itemize the whole doc.
  4085. // (#6503) We cant undo the -1 format change in plaintext and that causes
  4086. // many problems when we undo ReplaceRange event happening before the paragraph
  4087. // switches. We better abandon the whole stack for now. (wchao)
  4088. // -FUTURE- We should create an antievent for -1 PF change.
  4089. ClearUndo(publdr);
  4090. }
  4091. _pdp->UpdateView();
  4092. if (_psel)
  4093. _psel->UpdateCaret(!Get10Mode() || _psel->IsCaretInView());
  4094. if(fMatchKbdToPara)
  4095. _psel->MatchKeyboardToPara();
  4096. return TRUE;
  4097. }
  4098. //////////////////////////////// System notifications ////////////////////////////////
  4099. LRESULT CTxtEdit::OnSetFocus()
  4100. {
  4101. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetFocus");
  4102. _fFocus = TRUE;
  4103. // Update our idea of the current keyboard layout
  4104. W32->RefreshKeyboardLayout();
  4105. InitKeyboardFlags();
  4106. if(!_psel)
  4107. return 0;
  4108. // _fMouseDown may sometimes be true.
  4109. // This can happen when somebody steals our focus when we were doing
  4110. // something with the mouse down--like processing a click. Thus, we'll
  4111. // never get the MouseUpMessage.
  4112. if(_fMouseDown)
  4113. {
  4114. TRACEWARNSZ("Getting the focus, yet we think the mouse is down");
  4115. }
  4116. _fMouseDown = FALSE;
  4117. BOOL fAutoKeyboard = _fAutoKeyboard; // Don't change keyboard on SetFocus
  4118. _fAutoKeyboard = FALSE;
  4119. // BUG FIX #5369
  4120. // Special case where we don't have a selection (or a caret). We need
  4121. // to display something on focus so display a caret
  4122. _psel->UpdateCaret(_fScrollCaretOnFocus, _psel->GetCch() == 0);
  4123. _fScrollCaretOnFocus = FALSE;
  4124. _psel->ShowSelection(TRUE);
  4125. // If there is an in-place active object, we need to set the focus to
  4126. // it. (In addition to the work that we do; this maintains compatibility
  4127. // with RichEdit 1.0).
  4128. if(_pobjmgr)
  4129. {
  4130. COleObject *pobj = _pobjmgr->GetInPlaceActiveObject();
  4131. if(pobj)
  4132. {
  4133. IOleInPlaceObject *pipobj;
  4134. if(pobj->GetIUnknown()->QueryInterface(IID_IOleInPlaceObject,
  4135. (void **)&pipobj) == NOERROR)
  4136. {
  4137. HWND hwnd;
  4138. pipobj->GetWindow(&hwnd);
  4139. if(hwnd)
  4140. SetFocus(hwnd);
  4141. pipobj->Release();
  4142. }
  4143. }
  4144. }
  4145. if(IsInPageView())
  4146. TxInvalidate();
  4147. TxNotify(EN_SETFOCUS, NULL);
  4148. _fAutoKeyboard = fAutoKeyboard; // Restore setting
  4149. return 0;
  4150. }
  4151. LRESULT CTxtEdit::OnKillFocus()
  4152. {
  4153. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnKillFocus");
  4154. StopMagellanScroll();
  4155. if(_pundo)
  4156. _pundo->StopGroupTyping();
  4157. if(_psel)
  4158. {
  4159. // Scroll back to beginning if necessary
  4160. if (_fScrollCPOnKillFocus)
  4161. {
  4162. bool fHideSelectionLocal = _fHideSelection;
  4163. // cannot hide Selection so cp=0 will be scroll into view.
  4164. _fHideSelection = 0;
  4165. OnSetSel(0, 0);
  4166. _fHideSelection = fHideSelectionLocal;
  4167. }
  4168. _psel->DeleteCaretBitmap(TRUE); // Delete caret bitmap if one exists
  4169. if(_fHideSelection)
  4170. _psel->ShowSelection(FALSE);
  4171. }
  4172. _fFocus = FALSE;
  4173. DestroyCaret();
  4174. TxNotify(EN_KILLFOCUS, NULL);
  4175. _fScrollCaretOnFocus = FALSE; // Just to be safe, clear this
  4176. return 0;
  4177. }
  4178. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  4179. void CTxtEdit::OnDumpPed()
  4180. {
  4181. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnDumpPed");
  4182. char sz[256];
  4183. CTxtSelection * const psel = GetSel();
  4184. SELCHANGE selchg;
  4185. CHARRANGE crg = {0, 0};
  4186. psel->SetSelectionInfo(&selchg);
  4187. LONG cPage = 0;
  4188. LONG nPage = 0;
  4189. if(_pdp->IsMultiLine() && IsInPageView())
  4190. {
  4191. LONG cPageMoved;
  4192. CTxtRange rg(this, 0, 0);
  4193. _pdp->GetPage(&nPage, 0, &crg);
  4194. rg.Set(crg.cpMin, 0);
  4195. rg.Expand(tomPage, NULL);
  4196. Assert(rg.GetCpMost() == crg.cpMost);
  4197. rg.Set(0, 0);
  4198. rg.Move(tomPage, tomForward, &cPageMoved);
  4199. rg.GetIndex(tomPage, &cPage);
  4200. Assert(cPageMoved == cPage ||
  4201. rg.GetCp() == GetAdjustedTextLength() && cPageMoved == cPage - 1);
  4202. }
  4203. wsprintfA(sz,
  4204. "cchText = %ld cchTextMost = %ld\r\n"
  4205. "cpSelActive = %ld cchSel = %ld\r\n"
  4206. "wSelType = %x # lines = %ld\r\n"
  4207. "SysDefLCID = %lx UserDefLCID = %lx\r\n"
  4208. "Page = %ld cPage = %ld\r\n"
  4209. "cpMinPage = %ld cpMostPage = %ld",
  4210. GetTextLength(), TxGetMaxLength(),
  4211. psel->GetCp(), psel->GetCch(),
  4212. selchg.seltyp, _pdp->LineCount(),
  4213. GetSystemDefaultLCID(), GetUserDefaultLCID(),
  4214. nPage, cPage, crg.cpMin, crg.cpMost
  4215. );
  4216. Tracef(TRCSEVINFO, "%s", sz);
  4217. MessageBoxA(0, sz, "ED", MB_OK);
  4218. }
  4219. #endif // DEBUG
  4220. ///////////////////////////// Scrolling Commands //////////////////////////////////////
  4221. HRESULT CTxtEdit::TxLineScroll(
  4222. LONG cli,
  4223. LONG cch)
  4224. {
  4225. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxLineScroll");
  4226. // Currently cch does nothing in the following call, so we ignore
  4227. // its translation from cach to cch (need to instantiate an rtp
  4228. // for the current line
  4229. _pdp->LineScroll(cli, cch);
  4230. return S_OK;
  4231. }
  4232. void CTxtEdit::OnScrollCaret()
  4233. {
  4234. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnScrollCaret");
  4235. if(_psel)
  4236. {
  4237. _psel->SetForceScrollCaret(TRUE);
  4238. _psel->UpdateCaret(TRUE);
  4239. _psel->SetForceScrollCaret(FALSE);
  4240. }
  4241. }
  4242. ///////////////////////////////// Editing messages /////////////////////////////////
  4243. void CTxtEdit::OnClear(
  4244. IUndoBuilder *publdr)
  4245. {
  4246. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnClear");
  4247. if(!_psel || TxGetReadOnly())
  4248. {
  4249. Beep();
  4250. return;
  4251. }
  4252. if(_psel->GetCch() && !IsProtected(WM_CLEAR, 0, 0))
  4253. {
  4254. _psel->StopGroupTyping();
  4255. _psel->ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
  4256. }
  4257. }
  4258. void CTxtEdit::Beep()
  4259. {
  4260. if(_fAllowBeep)
  4261. MessageBeep(0);
  4262. }
  4263. /////////////////////////////////// Miscellaneous ///////////////////////////////////////////
  4264. /*
  4265. * CTxtEdit::ItemizeDoc(publdr, cchRange)
  4266. *
  4267. * @mfunc
  4268. * Helper routine to itemize the cchRange size of document content
  4269. * called by various clients outside CTxtRange.
  4270. */
  4271. void CTxtEdit::ItemizeDoc(
  4272. IUndoBuilder * publdr,
  4273. LONG cchRange)
  4274. {
  4275. // If cchRange = -1, itemize the whole doc
  4276. if (cchRange == -1)
  4277. cchRange = GetTextLength();
  4278. // We wouldnt itemize if the doc only contains a single EOP
  4279. // because we dont want Check_rpPF being called when the -1
  4280. // PF format hasnt been properly established.
  4281. // This is kind of hack, should be removed in the future.
  4282. //
  4283. if(cchRange && GetAdjustedTextLength())
  4284. { // Only itemize if more than
  4285. CTxtRange rg(this, 0, -cchRange); // final EOP
  4286. rg.ItemizeRuns(publdr);
  4287. }
  4288. #if 0
  4289. // =FUTURE=
  4290. // Once we open SPF_SETDEFAULT to public. We shall incorporate this code.
  4291. // Basically, one can change the default paragraph reading order at runtime. All
  4292. // PF runs referencing to -1 PF format then need to be reassigned a new paragraph
  4293. // level value and reitemized.(6-10-99, wchao)
  4294. //
  4295. if(cchRange > 0)
  4296. {
  4297. CTxtRange rg(this, 0, -cchRange);
  4298. // -1 PF format may have changed.
  4299. // We shall make sure that the level of each PF run match the reading order
  4300. // before start itemization.
  4301. //
  4302. if (rg.Check_rpPF())
  4303. {
  4304. LONG cchLeft = cchRange;
  4305. LONG cchMove = 0;
  4306. LONG cch;
  4307. while (cchLeft > 0)
  4308. {
  4309. rg._rpPF.GetRun(0)->_level._value = rg.IsParaRTL() ? 1 : 0;
  4310. cch = rg._rpPF.GetCchLeft();
  4311. if (!rg._rpPF.NextRun())
  4312. break; // no more run
  4313. cchMove += cch;
  4314. cchLeft -= cch;
  4315. }
  4316. Assert (cchMove + cchLeft == cchRange);
  4317. rg._rpPF.Move(-cchMove); // fly back to cp = 0
  4318. }
  4319. // Now we rerun itemization
  4320. rg.ItemizeRuns(publdr);
  4321. }
  4322. #endif
  4323. }
  4324. /*
  4325. * CTxtEdit::OrCharFlags(dwFlags, publdr)
  4326. *
  4327. * @mfunc
  4328. * Or in new char flags and activate LineServices and Uniscribe
  4329. * if complex script chars occur.
  4330. */
  4331. void CTxtEdit::OrCharFlags(
  4332. QWORD qwFlags,
  4333. IUndoBuilder* publdr)
  4334. {
  4335. #ifndef NOCOMPLEXSCRIPTS
  4336. // REVIEW: Should we send a notification for LS turn on?
  4337. // Convert dwFlags to new on flags
  4338. qwFlags &= qwFlags ^ _qwCharFlags;
  4339. if(qwFlags)
  4340. {
  4341. _qwCharFlags |= qwFlags; // Update flags
  4342. qwFlags &= FCOMPLEX_SCRIPT;
  4343. if(qwFlags && (_qwCharFlags & FCOMPLEX_SCRIPT) == qwFlags)
  4344. {
  4345. // REVIEW: Need to check if Uniscribe and LineServices are available...
  4346. OnSetTypographyOptions(TO_ADVANCEDTYPOGRAPHY, TO_ADVANCEDTYPOGRAPHY);
  4347. ItemizeDoc();
  4348. // FUTURE: (#6838) We cant undo operations before the first itemization.
  4349. ClearUndo(publdr);
  4350. _fAutoKeyboard = IsBiDi();
  4351. }
  4352. UINT brk = 0;
  4353. if (qwFlags & FNEEDWORDBREAK)
  4354. brk += BRK_WORD;
  4355. if (qwFlags & FNEEDCHARBREAK)
  4356. brk += BRK_CLUSTER;
  4357. if (brk)
  4358. {
  4359. CUniscribe* pusp = Getusp();
  4360. if (!_pbrk && pusp && pusp->IsValid())
  4361. {
  4362. // First time detecting the script that needs word/cluster-breaker
  4363. // (such as Thai, Indic, Lao etc.)
  4364. _pbrk = new CTxtBreaker(this);
  4365. Assert(_pbrk);
  4366. }
  4367. if (_pbrk && _pbrk->AddBreaker(brk))
  4368. {
  4369. // Sync up the breaking array(s)
  4370. _pbrk->Refresh();
  4371. }
  4372. }
  4373. }
  4374. #endif
  4375. }
  4376. /*
  4377. * CTxtEdit::OnSetTypographyOptions(wparam, lparam)
  4378. *
  4379. * @mfunc
  4380. * If CTxtEdit isn't a password or accelerator control and wparam
  4381. * differs from _bTypography, update the latter and the view.
  4382. *
  4383. * @rdesc
  4384. * HRESULT = S_OK
  4385. */
  4386. HRESULT CTxtEdit::OnSetTypographyOptions(
  4387. WPARAM wparam, //@parm Typography flags
  4388. LPARAM lparam) //@parm Typography mask
  4389. {
  4390. // Validate params
  4391. if(wparam & ~(TO_SIMPLELINEBREAK | TO_ADVANCEDTYPOGRAPHY | TO_DISABLECUSTOMTEXTOUT | TO_ADVANCEDLAYOUT))
  4392. return E_INVALIDARG;
  4393. DWORD dwTypography = _bTypography & ~lparam; // Kill current flag values
  4394. dwTypography |= wparam & lparam; // Or in new values
  4395. if(_cpAccelerator == -1 && _bTypography != (BYTE)dwTypography)
  4396. {
  4397. _bTypography = (BYTE)dwTypography;
  4398. _pdp->InvalidateRecalc();
  4399. TxInvalidate();
  4400. }
  4401. return S_OK;
  4402. }
  4403. void CTxtEdit::TxGetViewInset(
  4404. RECTUV *prc,
  4405. const CDisplay *pdp) const
  4406. {
  4407. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetViewInset");
  4408. // Get inset, which is in HIMETRIC
  4409. RECTUV rcHimetric;
  4410. if(SUCCEEDED(_phost->TxGetViewInset((RECT*) &rcHimetric)))
  4411. {
  4412. if(!pdp) // If no display is specified,
  4413. pdp = _pdp; // use main display
  4414. AssertSz(pdp->IsValid(), "CTxtEdit::TxGetViewInset Device not valid");
  4415. prc->left = pdp->HimetricUtoDU(rcHimetric.left);
  4416. prc->top = pdp->HimetricVtoDV(rcHimetric.top);
  4417. prc->right = pdp->HimetricUtoDU(rcHimetric.right);
  4418. prc->bottom = pdp->HimetricVtoDV(rcHimetric.bottom);
  4419. }
  4420. else
  4421. {
  4422. // The call to the host failed. While this is highly improbable, we do
  4423. // want to something reasonably sensible. Therefore, we will just pretend
  4424. // there is no inset and continue.
  4425. ZeroMemory(prc, sizeof(RECTUV));
  4426. }
  4427. }
  4428. //
  4429. // helper functions. FUTURE (alexgo) maybe we should get rid of
  4430. // some of these
  4431. //
  4432. /* FUTURE (murrays): Unless they are called a lot, the TxGetBit routines
  4433. might be done more compactly as:
  4434. BOOL CTxtEdit::TxGetBit(
  4435. DWORD dwMask)
  4436. {
  4437. DWORD dwBits = 0;
  4438. _phost->TxGetPropertyBits(dwMask, &dwBits);
  4439. return dwBits != 0;
  4440. }
  4441. e.g., instead of TxGetSelectionBar(), we use TxGetBit(TXTBIT_SELECTIONBAR).
  4442. If they are called a lot (like TxGetSelectionBar()), the bits should probably
  4443. be cached, since that saves a bunch of cache misses incurred in going over to
  4444. the host.
  4445. */
  4446. BOOL CTxtEdit::IsLeftScrollbar() const
  4447. {
  4448. if(!_fHost2)
  4449. return FALSE;
  4450. #ifndef NOCOMPLEXSCRIPTS
  4451. DWORD dwStyle, dwExStyle;
  4452. _phost->TxGetWindowStyles(&dwStyle, &dwExStyle);
  4453. return dwExStyle & WS_EX_LEFTSCROLLBAR;
  4454. #else
  4455. return FALSE;
  4456. #endif
  4457. }
  4458. TXTBACKSTYLE CTxtEdit::TxGetBackStyle() const
  4459. {
  4460. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetBackStyle");
  4461. TXTBACKSTYLE style = TXTBACK_OPAQUE;
  4462. _phost->TxGetBackStyle(&style);
  4463. return style;
  4464. }
  4465. BOOL CTxtEdit::TxGetAutoSize() const
  4466. {
  4467. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetAutoSize");
  4468. return (_dwEventMask & ENM_REQUESTRESIZE);
  4469. }
  4470. BOOL CTxtEdit::TxGetAutoWordSel() const
  4471. {
  4472. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetAutoWordSel");
  4473. DWORD dwBits = 0;
  4474. _phost->TxGetPropertyBits(TXTBIT_AUTOWORDSEL, &dwBits);
  4475. return dwBits != 0;
  4476. }
  4477. DWORD CTxtEdit::TxGetMaxLength() const
  4478. {
  4479. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetMaxLength");
  4480. // Keep this a DWORD in case client uses a cpMost of 0xFFFFFFFF, which is
  4481. // admittedly a little large, at least for 32-bit address spaces!
  4482. // tomForward would be a more reasonable max length, altho it's also
  4483. // probably larger than possible in a 32-bit address space.
  4484. return _cchTextMost;
  4485. }
  4486. /*
  4487. * CTxtEdit::TxSetMaxToMaxText(LONG cExtra)
  4488. *
  4489. * @mfunc
  4490. * Set new maximum text length based on length of text and possibly extra chars
  4491. * to accomodate.
  4492. */
  4493. void CTxtEdit::TxSetMaxToMaxText(LONG cExtra)
  4494. {
  4495. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxSetMaxToMaxText");
  4496. // See if we need to update the text max
  4497. LONG cchRealLen = GetAdjustedTextLength() + cExtra;
  4498. if(_fInOurHost && _cchTextMost < (DWORD)cchRealLen)
  4499. _cchTextMost = cchRealLen;
  4500. }
  4501. WCHAR CTxtEdit::TxGetPasswordChar() const
  4502. {
  4503. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetPasswordChar");
  4504. if(_fUsePassword)
  4505. {
  4506. WCHAR ch = L'*';
  4507. _phost->TxGetPasswordChar(&ch);
  4508. // We don't allow these characters as password chars
  4509. if(ch < 32 || ch == WCH_EMBEDDING)
  4510. return L'*';
  4511. return ch;
  4512. }
  4513. return 0;
  4514. }
  4515. void CTxtEdit::TxGetClientRect(RECTUV *prc) const
  4516. {
  4517. RECT rc;
  4518. _phost->TxGetClientRect(&rc);
  4519. _pdp->RectuvFromRect(*prc, rc);
  4520. }
  4521. BOOL CTxtEdit::TxShowScrollBar(INT fnBar, BOOL fShow)
  4522. {
  4523. //Convert scrollbar bits from logical to physical
  4524. if (IsUVerticalTflow(_pdp->GetTflow()))
  4525. fnBar = fnBar == SB_HORZ ? SB_VERT : SB_HORZ;
  4526. return _phost->TxShowScrollBar(fnBar, fShow);
  4527. }
  4528. BOOL CTxtEdit::TxEnableScrollBar (INT fnBar, INT fuArrowFlags)
  4529. {
  4530. //Convert scrollbar bits from logical to physical
  4531. if (IsUVerticalTflow(_pdp->GetTflow()))
  4532. fnBar = fnBar == SB_HORZ ? SB_VERT : SB_HORZ;
  4533. return _phost->TxEnableScrollBar(fnBar, fuArrowFlags);
  4534. }
  4535. BOOL CTxtEdit::TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw)
  4536. {
  4537. //Convert scrollbar bits from logical to physical
  4538. if (IsUVerticalTflow(_pdp->GetTflow()))
  4539. fnBar = fnBar == SB_HORZ ? SB_VERT : SB_HORZ;
  4540. return _phost->TxSetScrollRange(fnBar, nMinPos, nMaxPos, fRedraw);
  4541. }
  4542. BOOL CTxtEdit::TxSetScrollPos (INT fnBar, INT nPos, BOOL fRedraw)
  4543. {
  4544. //Convert scrollbar bits from logical to physical
  4545. if (IsUVerticalTflow(_pdp->GetTflow()))
  4546. fnBar = fnBar == SB_HORZ ? SB_VERT : SB_HORZ;
  4547. return _phost->TxSetScrollPos(fnBar, nPos, fRedraw);
  4548. }
  4549. BOOL CTxtEdit::TxSetCaretPos(INT u, INT v)
  4550. {
  4551. POINTUV ptuv = {u, v};
  4552. POINT pt;
  4553. _pdp->PointFromPointuv(pt, ptuv);
  4554. return _phost->TxSetCaretPos(pt.x, pt.y);
  4555. }
  4556. DWORD CTxtEdit::TxGetScrollBars() const
  4557. {
  4558. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetScrollBars");
  4559. DWORD dwScroll;
  4560. _phost->TxGetScrollBars(&dwScroll);
  4561. //Convert scrollbar bits from physical to logical
  4562. if (IsUVerticalTflow(_pdp->GetTflow()))
  4563. {
  4564. DWORD dwScrollT = dwScroll;
  4565. dwScroll &= ~(WS_HSCROLL | WS_VSCROLL);
  4566. if (dwScrollT & WS_VSCROLL)
  4567. dwScroll |= WS_HSCROLL;
  4568. if (dwScrollT & WS_HSCROLL)
  4569. dwScroll |= WS_VSCROLL;
  4570. }
  4571. return dwScroll;
  4572. }
  4573. LONG CTxtEdit::TxGetSelectionBarWidth() const
  4574. {
  4575. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetSelectionBarWidth");
  4576. LONG lSelBarWidth = 0;
  4577. _phost->TxGetSelectionBarWidth(&lSelBarWidth);
  4578. return lSelBarWidth;
  4579. }
  4580. BOOL CTxtEdit::TxGetWordWrap() const
  4581. {
  4582. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetWordWrap");
  4583. DWORD dwBits = 0;
  4584. _phost->TxGetPropertyBits(TXTBIT_WORDWRAP, &dwBits);
  4585. return dwBits != 0;
  4586. }
  4587. BOOL CTxtEdit::TxGetSaveSelection() const
  4588. {
  4589. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::TxGetSaveSelection");
  4590. DWORD dwBits = 0;
  4591. _phost->TxGetPropertyBits(TXTBIT_SAVESELECTION, &dwBits);
  4592. return dwBits != 0;
  4593. }
  4594. /*
  4595. * CTxtEdit::ClearUndo()
  4596. *
  4597. * @mfunc Clear all undo buffers
  4598. */
  4599. void CTxtEdit::ClearUndo(
  4600. IUndoBuilder *publdr) //@parm the current undo context (may be NULL)
  4601. {
  4602. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::ClearUndo");
  4603. if(_pundo)
  4604. _pundo->ClearAll();
  4605. if(_predo)
  4606. _predo->ClearAll();
  4607. if(publdr)
  4608. publdr->Discard();
  4609. }
  4610. /////////////////////////////// ITextHost2 Extensions //////////////////////////////
  4611. /*
  4612. * CTxtEdit::TxIsDoubleClickPending ()
  4613. *
  4614. * @mfunc calls host via ITextHost2 to find out if double click is pending.
  4615. *
  4616. * @rdesc TRUE/FALSE
  4617. */
  4618. BOOL CTxtEdit::TxIsDoubleClickPending()
  4619. {
  4620. return _fHost2 ? _phost->TxIsDoubleClickPending() : FALSE;
  4621. }
  4622. /*
  4623. * CTxtEdit::TxGetWindow(phwnd)
  4624. *
  4625. * @mfunc calls host via ITextHost2 to get current window for this edit
  4626. * instance. This is very helpful for OLE object support
  4627. *
  4628. * @rdesc HRESULT
  4629. */
  4630. HRESULT CTxtEdit::TxGetWindow(
  4631. HWND *phwnd)
  4632. {
  4633. return _fHost2 ? _phost->TxGetWindow(phwnd) : E_NOINTERFACE;
  4634. }
  4635. /*
  4636. * CTxtEdit::TxSetForegroundWindow ()
  4637. *
  4638. * @mfunc calls host via ITextHost2 to make our window the foreground
  4639. * window. Used to support drag/drop.
  4640. *
  4641. * @rdesc HRESULT
  4642. */
  4643. HRESULT CTxtEdit::TxSetForegroundWindow()
  4644. {
  4645. return _fHost2 ? _phost->TxSetForegroundWindow() : E_NOINTERFACE;
  4646. }
  4647. /*
  4648. * CTxtEdit::TxGetPalette()
  4649. *
  4650. * @mfunc calls host via ITextHost2 to get current palette
  4651. *
  4652. * @rdesc HPALETTE
  4653. */
  4654. HPALETTE CTxtEdit::TxGetPalette()
  4655. {
  4656. return _fHost2 ? _phost->TxGetPalette() : NULL;
  4657. }
  4658. /*
  4659. * CTxtEdit::TxGetFEFlags(pFEFlags)
  4660. *
  4661. * @mfunc calls host via ITextHost2 to get current FE settings
  4662. *
  4663. * @rdesc HRESULT
  4664. */
  4665. HRESULT CTxtEdit::TxGetFEFlags(
  4666. LONG *pFEFlags)
  4667. {
  4668. #ifndef NOFEPROCESSING
  4669. *pFEFlags = 0; // In case no ITextHost2 methods
  4670. HRESULT hResult = _fHost2 ? _phost->TxGetFEFlags(pFEFlags) : E_NOINTERFACE;
  4671. if (hResult == NOERROR || hResult == E_NOINTERFACE)
  4672. {
  4673. if (Get10Mode())
  4674. *pFEFlags |= tomRE10Mode;
  4675. if (_fUseAtFont)
  4676. *pFEFlags |= tomUseAtFont;
  4677. if (_fUsePassword)
  4678. *pFEFlags |= tomUsePassword;
  4679. *pFEFlags |= (_pdp->GetTflow()) << 2;
  4680. }
  4681. return hResult;
  4682. #else
  4683. return E_NOINTERFACE;
  4684. #endif
  4685. }
  4686. /*
  4687. * CTxtEdit::TxSetCursor(hcur, fText)
  4688. *
  4689. * @mfunc calls host via ITextHost2 to set cursor
  4690. *
  4691. * @rdesc HCURSOR
  4692. */
  4693. HCURSOR CTxtEdit::TxSetCursor(
  4694. HCURSOR hcur,
  4695. BOOL fText)
  4696. {
  4697. return _fHost2 ? _phost->TxSetCursor2(hcur, fText) : ::SetCursor(hcur);
  4698. }
  4699. //
  4700. // Event Notification methods
  4701. //
  4702. /*
  4703. * CTxtEdit::TxNotify(iNotify, pv)
  4704. *
  4705. * @mfunc This function checks bit masks and sends notifications to the
  4706. * host.
  4707. *
  4708. * @devnote Callers should check to see if a special purpose notification
  4709. * method has already been provided.
  4710. *
  4711. * @rdesc S_OK, S_FALSE, or some error
  4712. */
  4713. HRESULT CTxtEdit::TxNotify(
  4714. DWORD iNotify, //@parm Notification to send
  4715. void *pv) //@parm Data associated with notification
  4716. {
  4717. // First, disallow notifications that we handle elsewhere
  4718. Assert(iNotify != EN_SELCHANGE); //see SetSelectionChanged
  4719. Assert(iNotify != EN_ERRSPACE); //see SetOutOfMemory
  4720. Assert(iNotify != EN_CHANGE); //see SetChangedEvent
  4721. Assert(iNotify != EN_HSCROLL); //see SendScrollEvent
  4722. Assert(iNotify != EN_VSCROLL); //see SendScrollEvent
  4723. Assert(iNotify != EN_MAXTEXT); //see SetMaxText
  4724. Assert(iNotify != EN_MSGFILTER); //this is handled specially
  4725. // in TxSendMessage
  4726. // Switch on the event to check masks.
  4727. DWORD dwMask;
  4728. switch(iNotify)
  4729. {
  4730. case EN_DROPFILES:
  4731. dwMask = ENM_DROPFILES;
  4732. goto Notify;
  4733. case EN_PROTECTED:
  4734. dwMask = ENM_PROTECTED;
  4735. goto Notify;
  4736. case EN_REQUESTRESIZE:
  4737. dwMask = ENM_REQUESTRESIZE;
  4738. goto Notify;
  4739. case EN_PARAGRAPHEXPANDED:
  4740. dwMask = ENM_PARAGRAPHEXPANDED;
  4741. goto Notify;
  4742. case EN_IMECHANGE:
  4743. if (!Get10Mode())
  4744. return S_FALSE;
  4745. dwMask = ENM_IMECHANGE;
  4746. goto Notify;
  4747. case EN_PAGECHANGE:
  4748. dwMask = ENM_PAGECHANGE;
  4749. goto Notify;
  4750. case EN_UPDATE:
  4751. if (!Get10Mode())
  4752. break;
  4753. dwMask = ENM_UPDATE;
  4754. //FALL THROUGH CASE
  4755. Notify:
  4756. if(!(_dwEventMask & dwMask))
  4757. return NOERROR;
  4758. }
  4759. return _phost->TxNotify(iNotify, pv);
  4760. }
  4761. /*
  4762. * CTxtEdit::SendScrollEvent(iNotify)
  4763. *
  4764. * @mfunc Sends scroll event if appropriate
  4765. *
  4766. * @comm Scroll events must be sent before any view updates have
  4767. * been requested and only if ENM_SCROLL is set.
  4768. */
  4769. void CTxtEdit::SendScrollEvent(
  4770. DWORD iNotify) //@parm Notification to send
  4771. {
  4772. Assert(iNotify == EN_HSCROLL || iNotify == EN_VSCROLL);
  4773. // FUTURE (alexgo/ricksa). The display code can't really
  4774. // handle this assert yet. Basically, we're trying to
  4775. // say that scrollbar notifications have to happen
  4776. // _before_ the window is updated. When we do the
  4777. // display rewrite, try to handle this better.
  4778. // Assert(_fUpdateRequested == FALSE);
  4779. if(_dwEventMask & ENM_SCROLL)
  4780. _phost->TxNotify(iNotify, NULL);
  4781. }
  4782. /*
  4783. * CTxtEdit::HandleLowFiRTF (szControl)
  4784. *
  4785. * @mfunc Handles sending EN_LOWFIRTF notifications.
  4786. *
  4787. * @rdesc TRUE if the EN_LOWFIRTF message was sent and
  4788. * processed successfully.
  4789. */
  4790. BOOL CTxtEdit::HandleLowFiRTF(
  4791. char * szControl) //@parm RTF control word prompting notification
  4792. {
  4793. if(!(_dwEventMask & ENM_LOWFIRTF))
  4794. return FALSE;
  4795. ENLOWFIRTF enLowFiRTF;
  4796. ZeroMemory(&enLowFiRTF, sizeof(enLowFiRTF));
  4797. enLowFiRTF.nmhdr.code = EN_LOWFIRTF;
  4798. enLowFiRTF.szControl = szControl;
  4799. return _phost->TxNotify(EN_LOWFIRTF, &enLowFiRTF) == S_FALSE;
  4800. }
  4801. /*
  4802. * CTxtEdit::HandleLinkNotification (msg, wparam, lparam, pfInLink)
  4803. *
  4804. * @mfunc Handles sending EN_LINK notifications.
  4805. *
  4806. * @rdesc TRUE if the EN_LINK message was sent and
  4807. * processed successfully. Typically, that means the
  4808. * caller should stop whatever processing it was doing.
  4809. */
  4810. BOOL CTxtEdit::HandleLinkNotification(
  4811. UINT msg, //@parm msg prompting the link notification
  4812. WPARAM wparam, //@parm wparam of the message
  4813. LPARAM lparam, //@parm lparam of the message
  4814. BOOL * pfInLink) //@parm if non-NULL, indicate if over a link
  4815. {
  4816. if(pfInLink)
  4817. *pfInLink = FALSE;
  4818. if(!(_dwEventMask & ENM_LINK) || !_fInPlaceActive)
  4819. return FALSE;
  4820. LONG cp;
  4821. if(msg == WM_CHAR)
  4822. {
  4823. if(!_psel->GetCp() && !_psel->GetCch())
  4824. return FALSE;
  4825. _psel->_rpCF.AdjustBackward();
  4826. DWORD dwEffectsPrev = _psel->GetCF()->_dwEffects;
  4827. _psel->_rpCF.AdjustForward();
  4828. if (!(dwEffectsPrev & CFE_LINK) || (CFE_LINKPROTECTED | CFE_HIDDEN)
  4829. == (dwEffectsPrev & (CFE_LINKPROTECTED | CFE_HIDDEN)) ||
  4830. !(_psel->GetCF()->_dwEffects & CFE_LINK))
  4831. {
  4832. return FALSE;
  4833. }
  4834. cp = _psel->GetCp();
  4835. }
  4836. else
  4837. {
  4838. HITTEST Hit;
  4839. POINT ptxy = {LOWORD(lparam), HIWORD(lparam)};
  4840. POINTUV pt;
  4841. if(msg == WM_SETCURSOR)
  4842. {
  4843. GetCursorPos(&ptxy);
  4844. if(!_phost->TxScreenToClient(&ptxy))
  4845. return FALSE;
  4846. }
  4847. _pdp->PointuvFromPoint(pt, ptxy);
  4848. cp = _pdp->CpFromPoint(pt, NULL, NULL, NULL, FALSE, &Hit);
  4849. if(Hit != HT_Link) // Not a hyperlink
  4850. return FALSE;
  4851. }
  4852. LONG cpMin, cpMost; // It's a hyperlink
  4853. ENLINK enlink;
  4854. CTxtRange rg(this, cp, 0);
  4855. ZeroMemory(&enlink, sizeof(enlink));
  4856. enlink.nmhdr.code = EN_LINK;
  4857. if(pfInLink)
  4858. *pfInLink = TRUE;
  4859. rg.SetIgnoreFormatUpdate(TRUE);
  4860. rg.Expander(tomLink, TRUE, NULL, &cpMin, &cpMost);
  4861. //If the previous character of a link is hidden, then
  4862. //this came in as an RTF hyperlink field, so just export
  4863. //the hidden text to the client to be passed to the browser.
  4864. rg.SetCp(cpMin + 1, FALSE);
  4865. if (rg.GetCF()->_dwEffects & CFE_HIDDEN)
  4866. {
  4867. rg.Expander(tomHidden, TRUE, NULL, &cpMin, &cpMost);
  4868. rg.SetCp(cpMin, FALSE);
  4869. WCHAR ch;
  4870. //Go to end of hyperlink (search for k)
  4871. if (rg.CRchTxtPtr::FindText(cpMost, FR_DOWN, L"K", 1) == -1)
  4872. return FALSE;
  4873. cpMin = rg.GetCp();
  4874. //Strip off quotes and spaces
  4875. while ((ch = rg.CRchTxtPtr::GetChar()) == ' ' || ch == '\"')
  4876. {
  4877. rg.Move(1, FALSE);
  4878. cpMin++;
  4879. }
  4880. //Find end of hyperlink. Do not just start from the end as
  4881. //a fldinst can contain stuff which isn't part of the hyperlink
  4882. WCHAR chPrev = rg.CRchTxtPtr::GetPrevChar();
  4883. if (rg.CRchTxtPtr::FindText(cpMost, FR_DOWN, &chPrev, 1) == -1)
  4884. return FALSE;
  4885. cpMost = rg.GetCp() - 1;
  4886. }
  4887. // Fill in ENLINK data structure for our EN_LINK
  4888. // callback asking client what we should do
  4889. enlink.msg = msg;
  4890. enlink.wParam = wparam;
  4891. enlink.lParam = lparam;
  4892. enlink.chrg.cpMin = GetAcpFromCp(cpMin);
  4893. enlink.chrg.cpMost = GetAcpFromCp(cpMost);
  4894. if(msg == WM_CHAR) // Need to send both down and up
  4895. { // msgs, since Outlook responds
  4896. enlink.msg = WM_LBUTTONDOWN; // to down and others to up
  4897. _phost->TxNotify(EN_LINK, &enlink);
  4898. enlink.msg = WM_LBUTTONUP;
  4899. }
  4900. return _phost->TxNotify(EN_LINK, &enlink) == S_FALSE;
  4901. }
  4902. /*
  4903. * CTxtEdit::QueryUseProtection(prg, msg, wparam, lparam)
  4904. *
  4905. * @mfunc sends EN_PROTECTED to the host, asking if we should continue
  4906. * to honor the protection on a given range of characters
  4907. *
  4908. * @rdesc TRUE if protection should be honored, FALSE otherwise
  4909. */
  4910. BOOL CTxtEdit::QueryUseProtection(
  4911. CTxtRange *prg, //@parm range to check for
  4912. UINT msg, //@parm msg used
  4913. WPARAM wparam, //@parm wparam of the msg
  4914. LPARAM lparam) //@parm lparam of the msg
  4915. {
  4916. LONG cpMin, cpMost;
  4917. ENPROTECTED enp;
  4918. BOOL fRet = FALSE;
  4919. CCallMgr * pcallmgr = GetCallMgr();
  4920. Assert(_dwEventMask & ENM_PROTECTED);
  4921. if( pcallmgr->GetInProtected() ||
  4922. _fSuppressNotify) // Don't ask host if we don't want to send notification
  4923. return FALSE;
  4924. pcallmgr->SetInProtected(TRUE);
  4925. ZeroMemory(&enp, sizeof(ENPROTECTED));
  4926. prg->GetRange(cpMin, cpMost);
  4927. enp.msg = msg;
  4928. enp.wParam = wparam;
  4929. enp.lParam = lparam;
  4930. enp.chrg.cpMin = GetAcpFromCp(cpMin);
  4931. enp.chrg.cpMost = GetAcpFromCp(cpMost);
  4932. if(_phost->TxNotify(EN_PROTECTED, &enp) == S_FALSE)
  4933. fRet = TRUE;
  4934. pcallmgr->SetInProtected(FALSE);
  4935. return fRet;
  4936. }
  4937. #ifdef DEBUG
  4938. //This is a debug api used to dump the document runs.
  4939. //If a pointer to the ped is passed, it is saved and
  4940. //used. If NULL is passed, the previously saved ped
  4941. //pointer is used. This allows the "context" to be
  4942. //setup by a function that has access to the ped and
  4943. //DumpDoc can be called lower down in a function that
  4944. //does not have access to the ped.
  4945. extern "C" {
  4946. void DumpStory(void *ped)
  4947. {
  4948. static CTxtEdit *pedSave = (CTxtEdit *)ped;
  4949. if(pedSave)
  4950. {
  4951. CTxtStory * pStory = pedSave->GetTxtStory();
  4952. if(pStory)
  4953. pStory->DbgDumpStory();
  4954. CObjectMgr * pobjmgr = pedSave->GetObjectMgr();
  4955. if(pobjmgr)
  4956. pobjmgr->DbgDump();
  4957. }
  4958. }
  4959. }
  4960. #endif
  4961. /*
  4962. * CTxtEdit::TxGetDefaultCharFormat (pCF)
  4963. *
  4964. * @mfunc helper function to retrieve character formats from the
  4965. * host. Does relevant argument checking
  4966. *
  4967. * @rdesc HRESULT
  4968. */
  4969. HRESULT CTxtEdit::TxGetDefaultCharFormat(
  4970. CCharFormat *pCF, //@parm Character format to fill in
  4971. DWORD & dwMask) //@parm Mask supplied by host or default
  4972. {
  4973. HRESULT hr = pCF->InitDefault(0);
  4974. dwMask = CFM_ALL2;
  4975. const CHARFORMAT2 *pCF2 = NULL;
  4976. if (_phost->TxGetCharFormat((const CHARFORMAT **)&pCF2) != NOERROR ||
  4977. !IsValidCharFormatW(pCF2))
  4978. {
  4979. return hr;
  4980. }
  4981. dwMask = pCF2->dwMask;
  4982. DWORD dwMask2 = 0;
  4983. if(pCF2->cbSize == sizeof(CHARFORMAT))
  4984. {
  4985. // Suppress CHARFORMAT2 specifications (except for Forms^3 disabled)
  4986. dwMask &= fInOurHost() ? CFM_ALL : (CFM_ALL | CFM_DISABLED);
  4987. dwMask2 = CFM2_CHARFORMAT;
  4988. }
  4989. CCharFormat CF; // Transfer external CHARFORMAT(2)
  4990. CF.Set(pCF2, 1200); // parms to internal CCharFormat
  4991. return pCF->Apply(&CF, dwMask, dwMask2);
  4992. }
  4993. /*
  4994. * CTxtEdit::TxGetDefaultParaFormat (pPF)
  4995. *
  4996. * @mfunc helper function to retrieve paragraph formats. Does
  4997. * the relevant argument checking.
  4998. *
  4999. * @rdesc HRESULT
  5000. */
  5001. HRESULT CTxtEdit::TxGetDefaultParaFormat(
  5002. CParaFormat *pPF) //@parm Paragraph format to fill in
  5003. {
  5004. HRESULT hr = pPF->InitDefault(0);
  5005. const PARAFORMAT2 *pPF2 = NULL;
  5006. if (_phost->TxGetParaFormat((const PARAFORMAT **)&pPF2) != NOERROR ||
  5007. !IsValidParaFormat(pPF2))
  5008. {
  5009. return hr;
  5010. }
  5011. DWORD dwMask = pPF2->dwMask;
  5012. DWORD dwMask2 = 0;
  5013. if(pPF2->cbSize == sizeof(PARAFORMAT)) // Suppress all but PARAFORMAT
  5014. { // specifications
  5015. dwMask &= PFM_ALL;
  5016. dwMask2 = PFM2_PARAFORMAT; // Tell Apply() that PARAFORMAT
  5017. } // was used
  5018. CParaFormat PF; // Transfer external PARAFORMAT(2)
  5019. PF.Set(pPF2); // parms to internal CParaFormat
  5020. return pPF->Apply(&PF, dwMask, dwMask2);// Apply parms identified by dwMask
  5021. }
  5022. /*
  5023. * CTxtEdit::SetContextDirection(fUseKbd)
  5024. *
  5025. * @mfunc
  5026. * Determine the paragraph direction and/or alignment based on the context
  5027. * rules (direction/alignment follows first strong character in the
  5028. * control) and apply this direction and/or alignment to the default
  5029. * format.
  5030. *
  5031. * @comment
  5032. * Context direction only works for plain text controls. Note that
  5033. * this routine only switches the default CParaFormat to RTL para if it
  5034. * finds an RTL char. IsBiDi() will automatically be TRUE for this case,
  5035. * since each char is checked before entering the backing store.
  5036. */
  5037. void CTxtEdit::SetContextDirection(
  5038. BOOL fUseKbd) //@parm Use keyboard to set context when CTX_NEUTRAL
  5039. {
  5040. // It turns out that Forms^3 can send EM_SETBIDIOPTIONS even for non BiDi controls.
  5041. // AssertSz(IsBiDi(), "CTxtEdit::SetContextDirection called for nonBiDi control");
  5042. if(IsRich() || !IsBiDi() || _nContextDir == CTX_NONE && _nContextAlign == CTX_NONE)
  5043. return;
  5044. LONG cch = GetTextLength();
  5045. CTxtPtr tp(this, 0);
  5046. WCHAR ch = tp.GetChar();
  5047. WORD ctx = CTX_NEUTRAL;
  5048. BOOL fChanged = FALSE;
  5049. // Find first strongly directional character
  5050. while (cch && !IsStrongDirectional(MECharClass(ch)))
  5051. {
  5052. ch = tp.NextChar();
  5053. cch--;
  5054. }
  5055. // Set new context based on first strong character
  5056. // if no strong charactes in ctrl and have Bidi keybd, then make RTL
  5057. if(cch)
  5058. ctx = IsRTL(MECharClass(ch)) ? CTX_RTL : CTX_LTR;
  5059. else
  5060. ctx = (W32->IsBiDiLcid(LOWORD(GetKeyboardLayout(0)))) ? CTX_RTL : CTX_LTR;
  5061. // Has context direction or alignment changed?
  5062. if (_nContextDir != CTX_NONE && _nContextDir != ctx ||
  5063. _nContextAlign != CTX_NONE && _nContextAlign != ctx)
  5064. {
  5065. // Start with current default CParaFormat
  5066. CParaFormat PF = *GetParaFormat(-1);
  5067. // If direction has changed...
  5068. if(_nContextDir != CTX_NONE && _nContextDir != ctx)
  5069. {
  5070. if(ctx == CTX_LTR || ctx == CTX_RTL || fUseKbd)
  5071. {
  5072. if (ctx == CTX_RTL ||
  5073. ctx == CTX_NEUTRAL && W32->IsBiDiLcid(LOWORD(GetKeyboardLayout(0))))
  5074. {
  5075. PF._wEffects |= PFE_RTLPARA;
  5076. }
  5077. else
  5078. {
  5079. Assert(ctx == CTX_LTR || ctx == CTX_NEUTRAL);
  5080. PF._wEffects &= ~PFE_RTLPARA;
  5081. }
  5082. fChanged = TRUE;
  5083. }
  5084. _nContextDir = ctx;
  5085. }
  5086. // If the alignment has changed...
  5087. if(_nContextAlign != CTX_NONE && _nContextAlign != ctx)
  5088. {
  5089. if(PF._bAlignment != PFA_CENTER)
  5090. {
  5091. if(ctx == CTX_LTR || ctx == CTX_RTL || fUseKbd)
  5092. {
  5093. if (ctx == CTX_RTL ||
  5094. ctx == CTX_NEUTRAL && W32->IsBiDiLcid(LOWORD(GetKeyboardLayout(0))))
  5095. {
  5096. PF._bAlignment = PFA_RIGHT;
  5097. }
  5098. else
  5099. {
  5100. Assert(ctx == CTX_LTR || ctx == CTX_NEUTRAL);
  5101. PF._bAlignment = PFA_LEFT;
  5102. }
  5103. }
  5104. }
  5105. _nContextAlign = ctx;
  5106. }
  5107. // Modify default CParaFormat
  5108. IParaFormatCache *pPFCache = GetParaFormatCache();
  5109. LONG iPF;
  5110. if(SUCCEEDED(pPFCache->Cache(&PF, &iPF)))
  5111. {
  5112. pPFCache->Release(Get_iPF()); // Release _iPF regardless of
  5113. Set_iPF(iPF); // Update default format index
  5114. if (fChanged)
  5115. ItemizeDoc(NULL);
  5116. // Refresh display
  5117. Assert(_pdp);
  5118. if(!_pdp->IsPrinter())
  5119. {
  5120. _pdp->InvalidateRecalc();
  5121. TxInvalidate();
  5122. }
  5123. }
  5124. }
  5125. // Reset the first strong cp.
  5126. _cpFirstStrong = tp.GetCp();
  5127. Assert(_nContextDir != CTX_NONE || _nContextAlign != CTX_NONE);
  5128. }
  5129. /*
  5130. * CTxtEdit::GetAdjustedTextLength ()
  5131. *
  5132. * @mfunc
  5133. * retrieve text length adjusted for the default end-of-document marker
  5134. *
  5135. * @rdesc
  5136. * Text length without final EOP
  5137. *
  5138. * @devnote
  5139. * For Word and RichEdit compatibility, we insert a CR or CRLF at the
  5140. * end of every new rich-text control. This routine calculates the
  5141. * length of the document _without_ this final EOD marker.
  5142. *
  5143. * For 1.0 compatibility, we insert a CRLF. However, TOM (and Word)
  5144. * requires that we use a CR, from 2.0 on, we do that instead.
  5145. */
  5146. LONG CTxtEdit::GetAdjustedTextLength()
  5147. {
  5148. LONG cchAdjText = GetTextLength();
  5149. Assert(!Get10Mode() || IsRich()); // No RE10 plain-text controls
  5150. if(IsRich())
  5151. cchAdjText -= fUseCRLF() ? 2 : 1; // Subtract cch of final EOP
  5152. return cchAdjText;
  5153. }
  5154. /*
  5155. * CTxtEdit::Set10Mode()
  5156. *
  5157. * @mfunc
  5158. * Turns on the 1.0 compatibility mode bit. If the control is
  5159. * rich text, it already has a default 'CR' at the end, which
  5160. * needs to turn into a CRLF for compatibility with RichEdit 1.0.
  5161. *
  5162. * @devnote
  5163. * This function should only be called _immediately_ after
  5164. * creation of text services and before all other work. There
  5165. * are Asserts to help ensure this. Remark (murrays): why not
  5166. * allow the change provided the control is empty except for the
  5167. * final CR?
  5168. *
  5169. * FUTURE: we might want to split _f10Mode into three flags:
  5170. * 1) _fMapCps // API cp's are MBCS and need conversion to Unicode
  5171. * 2) _fCRLF // Use CRLFs for EOPs instead of CRs
  5172. * 3) _f10Mode // All other RE 1.0 compatibility things
  5173. *
  5174. * Category 3 includes 1) automatically using FR_DOWN in searches,
  5175. * 2) ignoring direction in CDataTransferObj::EnumFormatEtc(),
  5176. * 3) not resetting _fModified when switching to a new doc,
  5177. */
  5178. void CTxtEdit::Set10Mode()
  5179. {
  5180. CCallMgr callmgr(this);
  5181. _f10Mode = TRUE;
  5182. // Make sure nothing important has happened to the control.
  5183. // If these values are non-NULL, then somebody is probably trying
  5184. // to put us into 1.0 mode after we've already done work as
  5185. // a 2.0 control.
  5186. Assert(GetTextLength() == cchCR);
  5187. Assert(_psel == NULL);
  5188. Assert(_fModified == NULL);
  5189. SetRichDocEndEOP(cchCR);
  5190. if(!_pundo)
  5191. CreateUndoMgr(1, US_UNDO);
  5192. if(_pundo)
  5193. ((CUndoStack *)_pundo)->EnableSingleLevelMode();
  5194. // Turn off dual font
  5195. _fDualFont = FALSE;
  5196. // Turn on auto sizing for NTFE systems
  5197. if (OnWinNTFE())
  5198. _fAutoFontSizeAdjust = TRUE;
  5199. }
  5200. /*
  5201. * CTxtEdit::SetRichDocEndEOP(cchToReplace)
  5202. *
  5203. * @mfunc Place automatic EOP at end of a rich text document.
  5204. */
  5205. void CTxtEdit::SetRichDocEndEOP(
  5206. LONG cchToReplace)
  5207. {
  5208. CRchTxtPtr rtp(this, 0);
  5209. // Assume this is a 2.0 Doc
  5210. LONG cchEOP = cchCR;
  5211. const WCHAR *pszEOP = szCR;
  5212. if(_f10Mode)
  5213. {
  5214. // Reset update values for a 1.0 doc
  5215. cchEOP = cchCRLF;
  5216. pszEOP = szCRLF;
  5217. }
  5218. rtp.ReplaceRange(cchToReplace, cchEOP, pszEOP, NULL, -1);
  5219. _fModified = FALSE;
  5220. _fSaved = TRUE;
  5221. GetCallMgr()->ClearChangeEvent();
  5222. }
  5223. /*
  5224. * CTxtEdit::PopAndExecuteAntiEvent(pundomgr, void *pAE)
  5225. *
  5226. * @mfunc Freeze display and execute anti-event
  5227. *
  5228. * @rdesc HRESULT from IUndoMgr::PopAndExecuteAntiEvent
  5229. */
  5230. HRESULT CTxtEdit::PopAndExecuteAntiEvent(
  5231. IUndoMgr *pundomgr, //@parm Undo manager to direct call to
  5232. void *pAE) //@parm AntiEvent for undo manager
  5233. {
  5234. if(!pundomgr || _fReadOnly || !_fUseUndo || !pundomgr->CanUndo())
  5235. return S_FALSE;
  5236. if(_fReadOnly)
  5237. return E_ACCESSDENIED;
  5238. HRESULT hr;
  5239. // Let stack based classes clean up before restoring selection
  5240. {
  5241. CFreezeDisplay fd(_pdp);
  5242. CSelPhaseAdjuster selpa(this);
  5243. hr = pundomgr->PopAndExecuteAntiEvent(pAE);
  5244. }
  5245. if(_psel)
  5246. {
  5247. // Once undo/redo has been executed, flush insertion point formatting
  5248. _psel->Update_iFormat(-1);
  5249. _psel->Update(TRUE);
  5250. }
  5251. return hr;
  5252. }
  5253. /*
  5254. * CTxtEdit::PasteDataObjectToRange(pdo, prg, cf, rps, publdr, dwFlags)
  5255. *
  5256. * @mfunc Freeze display and paste object
  5257. *
  5258. * @rdesc HRESULT from IDataTransferEngine::PasteDataObjectToRange
  5259. */
  5260. HRESULT CTxtEdit::PasteDataObjectToRange(
  5261. IDataObject * pdo,
  5262. CTxtRange * prg,
  5263. CLIPFORMAT cf,
  5264. REPASTESPECIAL *rps,
  5265. IUndoBuilder * publdr,
  5266. DWORD dwFlags)
  5267. {
  5268. HRESULT hr = _ldte.PasteDataObjectToRange(pdo, prg, cf, rps, publdr,
  5269. dwFlags);
  5270. if(_psel)
  5271. {
  5272. #ifdef DEBUG
  5273. _psel->Invariant();
  5274. #endif
  5275. _psel->Update(TRUE); // now update the caret
  5276. }
  5277. return hr;
  5278. }
  5279. /*
  5280. * GetECDefaultHeightAndWidth (pts, hdc, lZoomNumerator, lZoomDenominator,
  5281. * yPixelsPerInch, pxAveWidth, pxOverhang, pxUnderhang)
  5282. *
  5283. * @mfunc Helper for host to get ave char width and height for default
  5284. * character set for the control.
  5285. *
  5286. * @rdesc Height of default character set
  5287. *
  5288. * @devnote:
  5289. * This really only s/b called by the window's host.
  5290. */
  5291. LONG GetECDefaultHeightAndWidth(
  5292. ITextServices *pts, //@parm ITextServices to conver to CTxtEdit.
  5293. HDC hdc, //@parm DC to use for retrieving the font.
  5294. LONG lZoomNumerator, //@parm Zoom numerator
  5295. LONG lZoomDenominator, //@parm Zoom denominator
  5296. LONG yPixelsPerInch, //@parm Pixels per inch for hdc
  5297. LONG *pxAveWidth, //@parm Optional ave width of character
  5298. LONG *pxOverhang, //@parm Optional overhang
  5299. LONG *pxUnderhang) //@parm Optional underhang
  5300. {
  5301. CLock lock; // Uses global (shared) FontCache
  5302. // Convert the text-edit ptr
  5303. CTxtEdit *ped = (CTxtEdit *) pts;
  5304. // Get the CCcs that has all the information we need
  5305. yPixelsPerInch = MulDiv(yPixelsPerInch, lZoomNumerator, lZoomDenominator);
  5306. CCcs *pccs = ped->GetCcs(ped->GetCharFormat(-1), yPixelsPerInch);
  5307. if(!pccs)
  5308. return 0;
  5309. if(pxAveWidth)
  5310. *pxAveWidth = pccs->_xAveCharWidth;
  5311. if(pxOverhang)
  5312. {
  5313. Assert(pxUnderhang);
  5314. pccs->GetFontOverhang(pxOverhang, pxUnderhang);
  5315. }
  5316. SHORT yAdjustFE = pccs->AdjustFEHeight(!ped->fUseUIFont() && ped->_pdp->IsMultiLine());
  5317. LONG yHeight = pccs->_yHeight + (yAdjustFE << 1);
  5318. pccs->Release(); // Release the CCcs
  5319. return yHeight;
  5320. }
  5321. /*
  5322. * CTxtEdit::TxScrollWindowEx (dx, dy, lprcScroll, lprcClip, hrgnUpdate,
  5323. * lprcUpdate, fupScroll)
  5324. * @mfunc
  5325. * Request Text Host to scroll the content of the specified client area
  5326. *
  5327. * @comm
  5328. * This method is only valid when the control is in-place active;
  5329. * calls while inactive may fail.
  5330. */
  5331. void CTxtEdit::TxScrollWindowEx(
  5332. INT dx, //@parm Amount of horizontal scrolling
  5333. INT dy, //@parm Amount of vertical scrolling
  5334. LPCRECT lprcScroll, //@parm Scroll rectangle
  5335. LPCRECT lprcClip) //@parm Clip rectangle
  5336. {
  5337. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEEXTERN, "CTxtEdit::TxScrollWindowEx");
  5338. if(_fInPlaceActive)
  5339. {
  5340. #if !defined(NOMAGELLAN)
  5341. CMagellanBMPStateWrap bmpOff(*this, NULL);
  5342. #endif
  5343. _phost->TxScrollWindowEx(dx, dy, lprcScroll, lprcClip, 0, 0, SW_INVALIDATE | SW_SCROLLCHILDREN);
  5344. }
  5345. }
  5346. /*
  5347. * CTxtEdit::GetAcpFromCp (cp)
  5348. *
  5349. * @mfunc
  5350. * Get API cp (acp) from Unicode cp in this text instance. The API cp
  5351. * may be Unicode, in which case it equals cp, or MBCS, in which case
  5352. * it's greater than cp if any Unicode characters preceding cp convert
  5353. * to double-byte characters. An MBCS cp is the BYTE index of a character
  5354. * relative to the start of the story, while a Unicode cp is the character
  5355. * index. The values are the same if all charsets are represented by
  5356. * SBCS charsets, e.g., ASCII. If all characters are represented by
  5357. * double-byte characters, then acp = 2*cp.
  5358. *
  5359. * @rdesc
  5360. * MBCS Acp from Unicode cp in this text instance
  5361. *
  5362. * @devnote
  5363. * This could be made more efficient by having the selection maintain
  5364. * the acp that corresponds to its _rpTX._cp, provided RE 1.0 mode is
  5365. * active. Alternatively CTxtEdit could have a _prg that tracks this
  5366. * value, but at a higher cost (17 DWORDs instead of 1 per instance).
  5367. *
  5368. * FUTURE: we might want to have a conversion-mode state instead of just
  5369. * _f10Mode, since some people might want to know use MBCS cp's even in
  5370. * RE 3.0. If so, use the corresponding new state flag instead of
  5371. * Get10Mode() in the following.
  5372. */
  5373. LONG CTxtEdit::GetAcpFromCp(
  5374. LONG cp, //@parm Unicode cp to convert to MBCS cp
  5375. BOOL fPrecise) //@parm fPrecise flag to get byte count for MBCS
  5376. {
  5377. if(!(IsFE() && (fCpMap() || fPrecise))) // RE 2.0 and higher use char-count
  5378. return cp; // cp's, while RE 1.0 uses byte
  5379. // counts
  5380. // bPrecise is for Ansi Apps that want byte counts
  5381. // (e.g. Outlook Subject line)
  5382. CRchTxtPtr rtp(this); // Start at cp = 0
  5383. return rtp.GetCachFromCch(cp);
  5384. }
  5385. LONG CTxtEdit::GetCpFromAcp(
  5386. LONG acp, //@parm MBCS cp to convert to Unicode cp
  5387. BOOL fPrecise) //@parm fPrecise flag to get Unicode cp for MBCS
  5388. {
  5389. if( acp == -1 || !(IsFE() && (fCpMap() || fPrecise)))
  5390. return acp;
  5391. CRchTxtPtr rtp(this); // Start at cp = 0
  5392. return rtp.GetCchFromCach(acp);
  5393. }
  5394. /*
  5395. * CTxtEdit::GetViewKind (plres)
  5396. *
  5397. * @mfunc
  5398. * get view mode
  5399. *
  5400. * @rdesc
  5401. * HRESULT = (plres) ? NOERROR : E_INVALIDARG
  5402. *
  5403. * @devnote
  5404. * This could be a TOM property method (along with SetViewMode())
  5405. */
  5406. HRESULT CTxtEdit::GetViewKind(
  5407. LRESULT *plres) //@parm Out parm to receive view mode
  5408. {
  5409. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetViewKind");
  5410. if(!plres)
  5411. return E_INVALIDARG;
  5412. *plres = IsInOutlineView() ? VM_OUTLINE
  5413. : IsInPageView() ? VM_PAGE : VM_NORMAL;
  5414. return NOERROR;
  5415. }
  5416. /*
  5417. * CTxtEdit::SetViewKind (Value)
  5418. *
  5419. * @mfunc
  5420. * Turn outline mode on or off
  5421. *
  5422. * @rdesc
  5423. * HRESULT = IsRich() ? NOERROR : S_FALSE
  5424. *
  5425. * @devnote
  5426. * This could be a TOM property method (along with GetViewMode())
  5427. */
  5428. HRESULT CTxtEdit::SetViewKind(
  5429. long Value) //@parm Turn outline mode on/off for Value nonzero/zero
  5430. {
  5431. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::SetViewKind");
  5432. CTxtSelection *psel = GetSel();
  5433. BOOL fPageView = Value == VM_PAGE && _pdp->IsMultiLine();
  5434. if(fPageView || Value == VM_NORMAL && IsInPageView())
  5435. {
  5436. _fPageView = (WORD)fPageView;
  5437. if(!IsInOutlineView())
  5438. {
  5439. _pdp->Paginate(0, TRUE);
  5440. psel->Update(TRUE);
  5441. TxInvalidate();
  5442. return NOERROR;
  5443. }
  5444. }
  5445. if(!IsRich() || !_pdp->IsMultiLine())
  5446. return S_FALSE;
  5447. Value = (Value == VM_OUTLINE); // Convert to 1/0
  5448. if(_fOutlineView != Value)
  5449. {
  5450. HCURSOR hcur = TxSetCursor(LoadCursor(0, IDC_WAIT), NULL);
  5451. _fOutlineView = (WORD)Value;
  5452. if(!GetAdjustedTextLength()) // No text in control: in outline
  5453. { // view, use Heading 1; in normal
  5454. CParaFormat PF; // view, use Normal style
  5455. PF._sStyle = (SHORT)(IsInOutlineView()
  5456. ? STYLE_HEADING_1 : STYLE_NORMAL);
  5457. psel->SetParaStyle(&PF, NULL, PFM_STYLE);
  5458. }
  5459. else
  5460. {
  5461. // There is text. Make sure there is paragraph formatting.
  5462. _psel->Check_rpPF();
  5463. }
  5464. psel->CheckIfSelHasEOP(-1, 0);
  5465. _pdp->UpdateView();
  5466. psel->Update(TRUE);
  5467. TxSetCursor(hcur, NULL);
  5468. }
  5469. return NOERROR;
  5470. }
  5471. /*
  5472. * CTxtEdit::GetViewScale (pValue)
  5473. *
  5474. * @mfunc
  5475. * get view zoom scale in percent
  5476. *
  5477. * @rdesc
  5478. * HRESULT = (pValue) ? NOERROR : E_INVALIDARG
  5479. *
  5480. * @devnote
  5481. * This could be a TOM property method (along with SetViewScale())
  5482. */
  5483. HRESULT CTxtEdit::GetViewScale(
  5484. long *pValue) //@parm Get % zoom factor
  5485. {
  5486. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetViewScale");
  5487. if(!pValue)
  5488. return E_INVALIDARG;
  5489. *pValue = 100;
  5490. if(GetZoomNumerator() && GetZoomDenominator())
  5491. *pValue = (100*GetZoomNumerator())/GetZoomDenominator();
  5492. return NOERROR;
  5493. }
  5494. /*
  5495. * CTxtEdit::SetViewScale (Value)
  5496. *
  5497. * @mfunc
  5498. * Set zoom numerator equal to the scale percentage Value and
  5499. * zoom denominator equal to 100
  5500. *
  5501. * @rdesc
  5502. * NOERROR
  5503. *
  5504. * @devnote
  5505. * This could be a TOM property method (along with GetViewScale())
  5506. */
  5507. HRESULT CTxtEdit::SetViewScale(
  5508. long Value) //@parm Set view scale factor
  5509. {
  5510. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::SetViewScale");
  5511. if((unsigned)Value > 2000)
  5512. return E_INVALIDARG;
  5513. SetZoomNumerator(Value);
  5514. SetZoomDenominator(100);
  5515. return NOERROR;
  5516. }
  5517. /*
  5518. * CTxtEdit::UpdateOutline()
  5519. *
  5520. * @mfunc
  5521. * Update selection and screen after ExpandOutline() operation
  5522. *
  5523. * @comm
  5524. * This method is only valid when the control is in-place active;
  5525. * calls while inactive may fail.
  5526. */
  5527. HRESULT CTxtEdit::UpdateOutline()
  5528. {
  5529. Assert(IsInOutlineView());
  5530. GetSel()->Update(FALSE);
  5531. TxInvalidate();
  5532. return NOERROR;
  5533. }
  5534. /*
  5535. * CTxtEdit::MoveSelection(lparam, publdr)
  5536. *
  5537. * @mfunc
  5538. * Move selected text up/down by the number of paragraphs given by
  5539. * LOWORD(lparam).
  5540. *
  5541. * @rdesc
  5542. * TRUE iff movement occurred
  5543. */
  5544. HRESULT CTxtEdit::MoveSelection (
  5545. LPARAM lparam, //@parm # paragraphs to move by
  5546. IUndoBuilder *publdr) //@parm undo builder to receive antievents
  5547. {
  5548. TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtRange::MoveSelection");
  5549. CFreezeDisplay fd(_pdp);
  5550. CTxtSelection * psel = GetSel();
  5551. LONG cch;
  5552. LONG cchSel = psel->GetCch();
  5553. LONG cpMin, cpMost;
  5554. LONG cpSel = psel->GetCp();
  5555. IDataObject * pdo = NULL;
  5556. CTxtRange rg(*psel);
  5557. LONG cpNext = 0;
  5558. LONG cpCur = 0;
  5559. BOOL fDeleteCR = FALSE;
  5560. if(publdr)
  5561. publdr->StopGroupTyping();
  5562. rg.Expander(tomParagraph, TRUE, NULL, &cpMin, &cpMost);
  5563. CPFRunPtr rp(rg);
  5564. cch = rp.FindExpanded(); // Include subordinate paras
  5565. if(cch < 0)
  5566. cch = tomForward;
  5567. rg.Move(cch, TRUE);
  5568. cpMost = rg.GetCpMost();
  5569. if(lparam > 0 && cpMost == GetTextLength())
  5570. {
  5571. Beep(); // Already at end
  5572. return S_FALSE;
  5573. }
  5574. HRESULT hr = _ldte.RangeToDataObject(&rg, SF_RTF, &pdo);
  5575. if(hr != NOERROR)
  5576. goto error;
  5577. if(lparam > 0)
  5578. psel->EndOf(tomParagraph, FALSE, NULL);
  5579. else
  5580. psel->StartOf(tomParagraph, FALSE, NULL);
  5581. cpCur = psel->GetCp();
  5582. hr = psel->Move(tomParagraph, lparam, NULL);
  5583. if(psel->GetCp() == cpCur)
  5584. {
  5585. psel->Set(cpSel, cchSel);
  5586. Beep();
  5587. goto error;
  5588. }
  5589. // Since psel->Move() calls psel->Update(), the selection is forced
  5590. // to be in noncollapsed text. Going backward, this might leave the
  5591. // selection just before the EOP of a paragraph, instead of being at the
  5592. // start of the paragraph where it should be. Going forward it may have
  5593. // tried to reach the EOD, but was adjusted backward. This case gets
  5594. // a bit awkward...
  5595. if(psel->GetCp() < cpCur) // Going backward: be sure
  5596. psel->StartOf(tomParagraph, FALSE, NULL);// end up at start of para
  5597. else if(!psel->_rpTX.IsAfterEOP()) // Going forward and sel
  5598. { // adjusted backward
  5599. psel->Move(tomForward, FALSE); // Go to final CR, insert a CR
  5600. CTxtRange rgDel(*psel); // use psel because UI
  5601. rgDel.ReplaceRange(1, szCR, publdr, SELRR_REMEMBERRANGE);
  5602. psel->Move(1, FALSE);
  5603. fDeleteCR = TRUE; // Remember to delete it
  5604. }
  5605. cpCur = psel->GetCp();
  5606. hr = _ldte.PasteDataObjectToRange(pdo, psel, 0, NULL,
  5607. publdr, PDOR_NONE);
  5608. if(hr != NOERROR)
  5609. goto error;
  5610. if(fDeleteCR) // Delete CR (final CR becomes
  5611. { // CR for this para). Don't
  5612. CTxtRange rgDel(*psel); // use psel because UI
  5613. Assert(rgDel._rpTX.IsAfterEOP()); // restricts it's ability to
  5614. rgDel.Delete(tomCharacter, -1, &cch); // delete
  5615. }
  5616. cpNext = psel->GetCp();
  5617. psel->Set(cpCur, 0);
  5618. psel->CheckOutlineLevel(publdr);
  5619. psel->Set(cpNext, 0);
  5620. psel->CheckOutlineLevel(publdr);
  5621. // Now set selection anti-events. If selection preceded paste point,
  5622. // subtract its length from redo position, since selection will get
  5623. // deleted if we are doing a DRAGMOVE within this instance.
  5624. cch = cpMost - cpMin; // cch of rg
  5625. if(cpSel < cpCur)
  5626. cpNext -= cch;
  5627. psel->Set(psel->GetCp() + fDeleteCR, cch); // Include final CR
  5628. // rg.ReplaceRange won't delete final CR, so remember if it's included
  5629. fDeleteCR = rg.GetCpMost() == GetTextLength();
  5630. rg.ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
  5631. if(fDeleteCR) // Needed to delete final CR
  5632. rg.DeleteTerminatingEOP(publdr); // Delete one immediately
  5633. // before it instead
  5634. rg.CheckOutlineLevel(publdr);
  5635. if(publdr)
  5636. {
  5637. HandleSelectionAEInfo(this, publdr, cpSel, cchSel, cpNext, cch,
  5638. SELAE_FORCEREPLACE);
  5639. }
  5640. hr = NOERROR;
  5641. error:
  5642. if(pdo)
  5643. pdo->Release();
  5644. return hr;
  5645. }
  5646. /*
  5647. * CTxtEdit::OnInsertTable(ptrp, pclp)
  5648. *
  5649. * @mfunc
  5650. * EM_INSERTTABLE acts similarly to EM_REPLACESEL for a degenerate
  5651. * selection (insertion point), but inserts a number of identical
  5652. * empty table rows instead of some plain text. Specifically
  5653. * it inserts ptrp->cRow empty table rows with the row and cell
  5654. * parameters given by ptrp and pclp, respectively. It leaves the
  5655. * selection pointing to the start of the first cell in the row. The
  5656. * client can then populate the table cells by pointing the selection
  5657. * at the cell end marks and inserting and formatting the desired text.
  5658. * Such text can include nested table rows, etc.
  5659. *
  5660. * The format for a table row is
  5661. *
  5662. * {CR ... }CR
  5663. *
  5664. * where { stands for STARTGROUP (0xFFF9), CR is 0xD, } stands for
  5665. * ENDGROUP (0xFFFB) and ... stands for TABLEROWPARMS::cCell cell-end
  5666. * marks. A cell-end mark is given by CELL (0x7), which is what Word
  5667. * also uses for this purposes. For example, a row with three cells has
  5668. * the plain text 0xFFF9 0xD 7 7 7 0xFFFB 0xD. The start and end group
  5669. * character pairs are assigned identical PARAFORMAT2 information that
  5670. * describes the row and cell parameters. If rows with different
  5671. * parameters are needed, multiple single-row calls can be made with the
  5672. * desired parameters.
  5673. *
  5674. * @rdesc
  5675. * HRESULT = S_OK if row inserted
  5676. */
  5677. HRESULT CTxtEdit::OnInsertTable(
  5678. TABLEROWPARMS * ptrp, //@parm Describes table row parameters
  5679. TABLECELLPARMS *pclp, //@parm Describes cell parameters
  5680. IUndoBuilder *publdr) //@parm Undo builder to receive antievents
  5681. {
  5682. CParaFormat PF;
  5683. CTxtSelection * pSel = GetSel();
  5684. CELLPARMS rgCellParms[MAX_TABLE_CELLS];
  5685. if (!ptrp || !pclp || !ptrp->cRow ||
  5686. !IN_RANGE(1, ptrp->cCell, MAX_TABLE_CELLS) ||
  5687. ptrp->cbRow != sizeof(TABLEROWPARMS) ||
  5688. ptrp->cbCell != sizeof(TABLECELLPARMS))
  5689. {
  5690. return E_INVALIDARG;
  5691. }
  5692. if(pSel->GetCch() || !IsRich() || !_pdp || !_pdp->IsMultiLine())
  5693. return E_FAIL;
  5694. LONG cpSelSave = pSel->GetCp();
  5695. pSel->StopGroupTyping();
  5696. while(pSel->GetPF()->IsTableRowDelimiter())
  5697. pSel->AdvanceCRLF(CSC_NORMAL, FALSE);
  5698. PF.InitDefault(0);
  5699. PF._bTabCount = ptrp->cCell;
  5700. PF._bAlignment = ptrp->nAlignment;
  5701. PF._dxOffset = ptrp->dxCellMargin;
  5702. PF._dxStartIndent = ptrp->dxIndent;
  5703. PF._dyLineSpacing = ptrp->dyHeight;
  5704. PF._wEffects = PFE_TABLE | PFE_TABLEROWDELIMITER;
  5705. PF._bTableLevel = pSel->GetPF()->_bTableLevel + 1;
  5706. if(ptrp->fRTL)
  5707. PF._wEffects |= PFE_RTLPARA;
  5708. if(ptrp->fKeep)
  5709. PF._wEffects |= PFE_KEEP;
  5710. if(ptrp->fKeepFollow)
  5711. PF._wEffects |= PFE_KEEPNEXT;
  5712. LONG uCell;
  5713. LONG dul = 0;
  5714. CCellColor ccr;
  5715. for(LONG i = 0; i < ptrp->cCell; i++)
  5716. {
  5717. uCell = pclp->dxWidth; // Cell width must be between
  5718. uCell = max(0, uCell); // 0" and 22"
  5719. uCell = min(1440*22, uCell);
  5720. dul += uCell;
  5721. if(dul > 1440*22)
  5722. return E_INVALIDARG;
  5723. uCell += (pclp->nVertAlign << 24);
  5724. if(pclp->fMergeTop)
  5725. uCell |= fTopCell;
  5726. else if(pclp->fMergePrev)
  5727. uCell |= fLowCell;
  5728. if(pclp->fVertical)
  5729. uCell |= fVerticalCell;
  5730. rgCellParms[i].uCell = uCell;
  5731. rgCellParms[i].dxBrdrWidths = (CheckTwips(pclp->dxBrdrLeft) << 0*8)
  5732. + (CheckTwips(pclp->dyBrdrTop) << 1*8)
  5733. + (CheckTwips(pclp->dxBrdrRight) << 2*8)
  5734. + (CheckTwips(pclp->dyBrdrBottom) << 3*8);
  5735. rgCellParms[i].dwColors = (ccr.GetColorIndex(pclp->crBrdrLeft) << 0*5)
  5736. + (ccr.GetColorIndex(pclp->crBrdrTop) << 1*5)
  5737. + (ccr.GetColorIndex(pclp->crBrdrRight) << 2*5)
  5738. + (ccr.GetColorIndex(pclp->crBrdrBottom)<< 3*5)
  5739. + (ccr.GetColorIndex(pclp->crBackPat) << 4*5)
  5740. + (ccr.GetColorIndex(pclp->crForePat) << 5*5);
  5741. if(pclp->wShading > 10000)
  5742. return E_INVALIDARG;
  5743. rgCellParms[i].bShading = (BYTE)(pclp->wShading/50);
  5744. if(!ptrp->fIdentCells)
  5745. pclp++;
  5746. }
  5747. if(ccr._crCellCustom1)
  5748. {
  5749. PF._crCustom1 = ccr._crCellCustom1;
  5750. if(ccr._crCellCustom2)
  5751. PF._crCustom2 = ccr._crCellCustom2;
  5752. }
  5753. PF._iTabs = GetTabsCache()->Cache((LONG *)&rgCellParms[0],
  5754. ptrp->cCell * (CELL_EXTRA + 1));
  5755. HRESULT hr = S_OK;
  5756. for(i = ptrp->cRow; i--; )
  5757. {
  5758. LONG cchCells = pSel->InsertTableRow(&PF, publdr);
  5759. if(!cchCells)
  5760. {
  5761. hr = E_FAIL;
  5762. break;
  5763. }
  5764. pSel->Move(cchCells + 2, FALSE); // Leave selection at end of row
  5765. }
  5766. GetTabsCache()->Release(PF._iTabs);
  5767. pSel->Update(TRUE);
  5768. if(publdr)
  5769. HandleSelectionAEInfo(this, publdr,
  5770. cpSelSave, 0, pSel->GetCp(), 0, SELAE_FORCEREPLACE);
  5771. return hr;
  5772. }
  5773. /*
  5774. * CTxtEdit::SetReleaseHost
  5775. *
  5776. * @mfunc Handles notification that edit control must keep its
  5777. * reference to the host alive.
  5778. */
  5779. void CTxtEdit::SetReleaseHost()
  5780. {
  5781. _phost->AddRef();
  5782. _fReleaseHost = TRUE;
  5783. }
  5784. #if !defined(NOMAGELLAN)
  5785. /*
  5786. * CTxtEdit::HandleMouseWheel(wparam, lparam)
  5787. *
  5788. * @mfunc Handles scrolling as a result of rotating a mouse roller wheel.
  5789. *
  5790. * @rdesc LRESULT
  5791. */
  5792. LRESULT CTxtEdit::HandleMouseWheel(
  5793. WPARAM wparam,
  5794. LPARAM lparam)
  5795. {
  5796. // This bit of global state is OK
  5797. static LONG gcWheelDelta = 0;
  5798. short zdelta = (short)HIWORD(wparam);
  5799. BOOL fScrollByPages = FALSE;
  5800. // Cancel middle mouse scrolling if it's going.
  5801. OnTxMButtonUp(0, 0, 0);
  5802. // Handle zoom or data zoom
  5803. if((wparam & MK_CONTROL) == MK_CONTROL)
  5804. {
  5805. // bug fix 5760
  5806. // prevent zooming if control is NOT rich or
  5807. // is a single line control
  5808. if (!_pdp->IsMultiLine())
  5809. return 0;
  5810. LONG lViewScale;
  5811. GetViewScale(&lViewScale);
  5812. lViewScale += (zdelta/WHEEL_DELTA) * 10; // 10% per click
  5813. if(lViewScale <= 500 && lViewScale >= 10) // Word's limits
  5814. {
  5815. SetViewScale(lViewScale);
  5816. _pdp->UpdateView();
  5817. }
  5818. return 0;
  5819. }
  5820. if(wparam & (MK_SHIFT | MK_CONTROL))
  5821. return 0;
  5822. gcWheelDelta += zdelta;
  5823. if(abs(gcWheelDelta) >= WHEEL_DELTA)
  5824. {
  5825. LONG cLineScroll = W32->GetRollerLineScrollCount();
  5826. if(cLineScroll != -1)
  5827. cLineScroll *= abs(gcWheelDelta)/WHEEL_DELTA;
  5828. gcWheelDelta %= WHEEL_DELTA;
  5829. // -1 means scroll by pages; so simply call page up/down.
  5830. if(cLineScroll == -1 || IsInPageView())
  5831. {
  5832. fScrollByPages = TRUE;
  5833. if(_pdp)
  5834. _pdp->VScroll(zdelta < 0 ? SB_PAGEDOWN : SB_PAGEUP, 0);
  5835. }
  5836. else
  5837. {
  5838. mouse.MagellanRollScroll(_pdp, zdelta, cLineScroll,
  5839. SMOOTH_ROLL_NUM, SMOOTH_ROLL_DENOM, TRUE);
  5840. }
  5841. // notify through the messagefilter that we scrolled
  5842. if(_dwEventMask & ENM_SCROLLEVENTS)
  5843. {
  5844. MSGFILTER msgfltr;
  5845. ZeroMemory(&msgfltr, sizeof(MSGFILTER));
  5846. msgfltr.msg = WM_VSCROLL;
  5847. msgfltr.wParam = fScrollByPages ?
  5848. (zdelta < 0 ? SB_PAGEDOWN: SB_PAGEUP):
  5849. (zdelta < 0 ? SB_LINEDOWN: SB_LINEUP);
  5850. // We don't check the result of this call --
  5851. // it's not a message we received and we're not going to
  5852. // process it any further
  5853. _phost->TxNotify(EN_MSGFILTER, &msgfltr);
  5854. }
  5855. return TRUE;
  5856. }
  5857. return 0;
  5858. }
  5859. #endif
  5860. const int cchCorrectMax = 256; //Max characters to be autocorrected (Office spec)
  5861. const int cchFromMax = 768;
  5862. /*
  5863. * CTxtEdit::AutoCorrect(psel, ch, publdr)
  5864. *
  5865. * @mfunc Call the client to autocorrect the recently added word. Don't replace
  5866. * the recently added character in string passed to client. (We don't want to
  5867. * ReplaceRange a CELL character, for example.)
  5868. */
  5869. void CTxtEdit::AutoCorrect(
  5870. CTxtSelection *psel,
  5871. WCHAR chAdd,
  5872. IUndoBuilder * publdr)
  5873. {
  5874. LONG cch = 0;
  5875. WCHAR pchFrom[cchFromMax + 1];
  5876. WCHAR pchTo[cchCorrectMax + 1];
  5877. CTxtPtr tp(psel->_rpTX);
  5878. WCHAR chPrev = tp.GetPrevChar();
  5879. BOOL fCheckIsLink = (L':' == chPrev || L'.' == chPrev) && GetDetectURL();
  5880. for(LONG i = 4; i-- && tp.GetCp(); )
  5881. {
  5882. LONG cchWord = -tp.FindWordBreak(WB_MOVEWORDLEFT);
  5883. if (i == 2 && fCheckIsLink)
  5884. {
  5885. BOOL fURLLeadin = FALSE;
  5886. GetDetectURL()->IsURL(tp, cchWord + 1, &fURLLeadin);
  5887. if(fURLLeadin)
  5888. return;
  5889. }
  5890. if(cch + cchWord > cchFromMax) // Don't bite off more than
  5891. break; // buffer can chew
  5892. cch += cchWord;
  5893. if (IsEOP(tp.GetPrevChar())) // Don't autocorrect across an EOP
  5894. break;
  5895. }
  5896. // Be sure we don't go into hyperlink or SYMBOL_CHARSET territory
  5897. if(psel->_rpCF.IsValid())
  5898. {
  5899. CCFRunPtr rp(*psel);
  5900. for(LONG cchMax = 0; cchMax < cch; rp.SetIch(0))
  5901. {
  5902. rp.AdjustBackward();
  5903. const CCharFormat *pCF = rp.GetCF();
  5904. if (pCF->_dwEffects & (CFE_LINK | CFE_HIDDEN) ||
  5905. pCF->_iCharRep == SYMBOL_INDEX)
  5906. {
  5907. break;
  5908. }
  5909. cchMax += rp.GetIch();
  5910. if(!rp.GetIRun())
  5911. break; // Reached start of doc
  5912. }
  5913. if(cchMax < cch) // Hyperlink within words to check
  5914. {
  5915. tp.Move(cch - cchMax); // Only check chars back to link,
  5916. cch = cchMax; // symbols, or CharRep change
  5917. }
  5918. }
  5919. tp.GetText(cch, pchFrom);
  5920. pchFrom[cch] = 0;
  5921. long cchTo = cchCorrectMax, cchReplaced = 0;
  5922. CCFRunPtr rp(*psel);
  5923. rp.Move(-2);
  5924. if(_pDocInfo->_pfnAutoCorrect(rp.GetCF()->_lcid, pchFrom, pchTo, cchTo, &cchReplaced))
  5925. {
  5926. // If plain text check for special cases to suppress per bug 8717.
  5927. // copyright, registered trademark, trademark, ellipses.
  5928. if(!_fRich && (pchTo[0] == 0xA9 || pchTo[0] == 0xAE ||
  5929. pchTo[0] == 0x2122 || pchTo[0] == 0x2026))
  5930. {
  5931. return;
  5932. }
  5933. if (publdr)
  5934. {
  5935. publdr->Done();
  5936. publdr->StopGroupTyping();
  5937. }
  5938. CTxtRange rg(*psel);
  5939. DWORD ch = rg.GetPrevChar();
  5940. LONG cpSave = psel->GetCp();
  5941. LONG cchDelim = 1;
  5942. if(ch >= 0x1100) // Most East Asian chars aren't delims
  5943. {
  5944. if(ch < 0x1200 || IN_RANGE(0x3040, ch, 0xD7FF) ||
  5945. ch >= 0xF900 && (ch < 0xFAFF || IN_RANGE(0xFF21, ch, 0xFFDF)))
  5946. {
  5947. cchDelim = 0;
  5948. }
  5949. }
  5950. rg.Set(rg.GetCp() - cchDelim, cchReplaced - cchDelim);
  5951. rg.CleanseAndReplaceRange(wcslen(pchTo) - cchDelim, pchTo, FALSE, publdr, pchTo);
  5952. if(!cchDelim)
  5953. psel->SetCp(rg.GetCp(), FALSE);
  5954. if (publdr)
  5955. HandleSelectionAEInfo(this, publdr, cpSave, 0, psel->GetCp(), 0, SELAE_FORCEREPLACE);
  5956. // publdr->SetNameID(UID_AUTOCORRECT);
  5957. }
  5958. }
  5959. /*
  5960. * CTxtEdit::OnSetAssociateFont(pCF, dwFlags)
  5961. *
  5962. * @mfunc Set the Associate font for the input LCID or charset
  5963. *
  5964. * @rdesc LRESULT
  5965. */
  5966. LRESULT CTxtEdit::OnSetAssociateFont(
  5967. CHARFORMAT2 *pCF2,
  5968. DWORD dwFlags)
  5969. {
  5970. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnSetAssociateFont");
  5971. int cpg = 0;
  5972. SHORT iFont;
  5973. BYTE yHeight;
  5974. int cbSize = pCF2->cbSize;
  5975. Assert(cbSize == sizeof(CHARFORMAT2W) || cbSize == sizeof(CHARFORMAT2A));
  5976. int iCharRep = CharRepFromCharSet(pCF2->bCharSet);
  5977. if (pCF2->dwMask & CFM_LCID)
  5978. {
  5979. iCharRep = CharRepFromLID(cbSize == sizeof(CHARFORMAT2W)
  5980. ? pCF2->lcid : ((CHARFORMAT2A *)pCF2)->lcid,
  5981. dwFlags & SCF_ASSOCIATEFONT2);
  5982. cpg = CodePageFromCharRep(iCharRep);
  5983. }
  5984. if (iCharRep == -1)
  5985. return 0; // Can't get Char repertoire, so forget it
  5986. if (cbSize == sizeof(CHARFORMAT2W))
  5987. iFont = GetFontNameIndex(pCF2->szFaceName);
  5988. else
  5989. {
  5990. // need to convert CHARFORMAT2A face name
  5991. LONG cch;
  5992. BOOL fMissingCodePage;
  5993. WCHAR szFaceName[LF_FACESIZE];
  5994. cch = MBTWC(cpg, 0,
  5995. ((CHARFORMAT2A *)pCF2)->szFaceName, -1,
  5996. szFaceName, LF_FACESIZE, &fMissingCodePage);
  5997. if (fMissingCodePage || cch <= 0)
  5998. return 0;
  5999. iFont = GetFontNameIndex(szFaceName);
  6000. }
  6001. yHeight = pCF2->yHeight / TWIPS_PER_POINT;
  6002. CLock lock;
  6003. if (W32->SetPreferredFontInfo(iCharRep, dwFlags & SCF_USEUIRULES ? true : false, iFont, yHeight, pCF2->bPitchAndFamily))
  6004. return 1;
  6005. return 0;
  6006. }
  6007. /*
  6008. * CTxtEdit::OnGetAssociateFont(pCF, dwFlags)
  6009. *
  6010. * @mfunc Get the Associate font for the input LCID or charset
  6011. *
  6012. * @rdesc LRESULT
  6013. */
  6014. LRESULT CTxtEdit::OnGetAssociateFont(
  6015. CHARFORMAT2 *pCF2,
  6016. DWORD dwFlags)
  6017. {
  6018. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnGetAssociateFont");
  6019. int cpg = 0;
  6020. SHORT iFont;
  6021. BYTE yHeight;
  6022. BYTE bPitchAndFamily;
  6023. int cbSize = pCF2->cbSize;
  6024. Assert(cbSize == sizeof(CHARFORMAT2W) || cbSize == sizeof(CHARFORMAT2A));
  6025. int iCharRep = CharRepFromCharSet(pCF2->bCharSet);
  6026. if (pCF2->dwMask & CFM_LCID)
  6027. {
  6028. iCharRep = CharRepFromLID(cbSize == sizeof(CHARFORMAT2W)
  6029. ? pCF2->lcid : ((CHARFORMAT2A *)pCF2)->lcid,
  6030. dwFlags & SCF_ASSOCIATEFONT2);
  6031. cpg = CodePageFromCharRep(iCharRep);
  6032. }
  6033. if (iCharRep == -1)
  6034. return 0; // Can't get char repertoire, so forget it
  6035. if (W32->GetPreferredFontInfo(iCharRep, dwFlags & SCF_USEUIRULES ? true : false, iFont, yHeight, bPitchAndFamily))
  6036. {
  6037. pCF2->yHeight = yHeight * TWIPS_PER_POINT;
  6038. pCF2->bPitchAndFamily = bPitchAndFamily;
  6039. if (cbSize == sizeof(CHARFORMAT2W))
  6040. wcscpy(pCF2->szFaceName, GetFontName((LONG)iFont));
  6041. else
  6042. {
  6043. // need to convert CHARFORMAT2A face name
  6044. LONG cch;
  6045. BOOL fMissingCodePage;
  6046. const WCHAR *pszFaceName = GetFontName((LONG)iFont);
  6047. cch = WCTMB(cpg, 0,
  6048. pszFaceName, -1,
  6049. ((CHARFORMAT2A *)pCF2)->szFaceName, LF_FACESIZE,
  6050. NULL, NULL, &fMissingCodePage);
  6051. if (fMissingCodePage || cch <= 0)
  6052. return 0;
  6053. }
  6054. return 1;
  6055. }
  6056. return 0;
  6057. }
  6058. #ifndef NOINKOBJECT
  6059. /*
  6060. * CTxtEdit::SetInkProps(ILineInfo *pILineInfo, UINT *piInkWidth)
  6061. *
  6062. * @mfunc Setup the Ink object properties
  6063. *
  6064. * @rdesc HRESULT
  6065. */
  6066. HRESULT CTxtEdit::SetInkProps(
  6067. LONG cp,
  6068. ILineInfo *pILineInfo,
  6069. UINT *piInkWidth)
  6070. {
  6071. HRESULT hr = E_FAIL;
  6072. INKMETRIC inkMetric;
  6073. CTxtRange rg(this, cp, 1);
  6074. const CCharFormat *pCF = rg.GetCF();
  6075. if (pCF)
  6076. {
  6077. memset(&inkMetric, 0, sizeof(inkMetric));
  6078. if (pCF->_wWeight > FW_NORMAL)
  6079. inkMetric.iWeight = 3; // Bold
  6080. inkMetric.fItalic = !!(pCF->_dwEffects & CFE_ITALIC); // Italic
  6081. // Height in HIMETRIC
  6082. inkMetric.iHeight = (UINT)MulDiv(pCF->_yHeight, HIMETRIC_PER_INCH, LY_PER_INCH);
  6083. inkMetric.color = pCF->_crTextColor; // Color
  6084. // Get zoomed height
  6085. LONG dvpInch = MulDiv(GetDeviceCaps(W32->GetScreenDC(), LOGPIXELSY), _pdp->GetZoomNumerator(), _pdp->GetZoomDenominator());
  6086. CCcs *pccs = GetCcs(pCF, dvpInch);
  6087. if (pccs)
  6088. {
  6089. inkMetric.iFontDescent = (UINT)MulDiv(inkMetric.iHeight, pccs->_yDescent, pccs->_yHeight);
  6090. inkMetric.iFontAscent = inkMetric.iHeight - inkMetric.iFontDescent;
  6091. // Release cache entry since we are done with it.
  6092. pccs->Release();
  6093. }
  6094. hr = pILineInfo->SetFormat(&inkMetric);
  6095. }
  6096. return hr;
  6097. }
  6098. #endif
  6099. /*
  6100. * CTxtEdit::GetCaretWidth()
  6101. *
  6102. * @mfunc Get caret width
  6103. *
  6104. * @rdesc
  6105. * caret width
  6106. */
  6107. HRESULT CTxtEdit::GetCaretWidth()
  6108. {
  6109. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetCaretWidth");
  6110. RECT rcInset;
  6111. if(!fInHost2() || // Host 1
  6112. SUCCEEDED(_phost->TxGetViewInset(&rcInset)) && !rcInset.right)
  6113. return duCaret;
  6114. return 0;
  6115. }
  6116. /*
  6117. * CCellColor::GetColorIndex(cr)
  6118. *
  6119. * @mfunc
  6120. * Get color index corresponding to cr. Possible return values are 0
  6121. * (autocolor), 1-16 (the 16 standard colors: g_Colors), and two custom
  6122. * colors, 17 and 18 defined on a first-come basis.
  6123. *
  6124. * @rdesc
  6125. * Color index corresponding to cr
  6126. */
  6127. LONG CCellColor::GetColorIndex(
  6128. COLORREF cr)
  6129. {
  6130. if(cr == tomAutoColor)
  6131. return 0;
  6132. for(LONG i = 0; i < 16; i++)
  6133. {
  6134. if(cr == g_Colors[i])
  6135. return i + 1;
  6136. }
  6137. if(!_crCellCustom1 || cr == _crCellCustom1)
  6138. {
  6139. _crCellCustom1 = cr; // First custom cr
  6140. return 17;
  6141. }
  6142. if(!_crCellCustom2 || cr == _crCellCustom2)
  6143. {
  6144. _crCellCustom2 = cr; // Second custom cr
  6145. return 18;
  6146. }
  6147. return 0; // No custom cr available
  6148. }