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.

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