Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

813 lines
26 KiB

  1. //*************************************************
  2. // r e u t i l . c p p
  3. //
  4. // Purpose:
  5. // implements calls to Richedit controls
  6. //
  7. // Owner:
  8. // dhaws
  9. //
  10. // History:
  11. // March 1999: Created
  12. //*************************************************
  13. #include "pch.hxx"
  14. #include "reutil.h"
  15. #include "richole.h"
  16. #include "richedit.h"
  17. #include "textserv.h"
  18. #include "shlwapip.h"
  19. #include "mirror.h"
  20. #include "strconst.h"
  21. #include <demand.h> // must be last!
  22. static HINSTANCE g_hRichEditDll = NULL;
  23. static DWORD g_dwRicheditVer = 0;
  24. // @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
  25. static BOOL g_fSpecialRTLRichedit = FALSE;
  26. // Keep these around for future reference
  27. static const CHAR g_szRE10[] = "RichEdit";
  28. static const CHAR g_szRE20[] = "RichEdit20A";
  29. static const WCHAR g_wszRE10[] = L"RichEdit";
  30. static const WCHAR g_wszRE20[] = L"RichEdit20W";
  31. BOOL FInitRichEdit(BOOL fInit)
  32. {
  33. if(fInit)
  34. {
  35. if(!g_hRichEditDll)
  36. {
  37. g_hRichEditDll = LoadLibrary("RICHED20.DLL");
  38. if (g_hRichEditDll)
  39. {
  40. TCHAR szPath[MAX_PATH],
  41. szGet[MAX_PATH];
  42. BOOL fSucceeded = FALSE;
  43. DWORD dwVerInfoSize = 0,
  44. dwVerHnd = 0,
  45. dwProdNum = 0,
  46. dwMajNum = 0,
  47. dwMinNum = 0;
  48. LPSTR lpInfo = NULL,
  49. lpVersion = NULL;
  50. LPWORD lpwTrans;
  51. UINT uLen;
  52. HKEY hkey;
  53. DWORD dw=0,
  54. cb;
  55. if (GetModuleFileName(g_hRichEditDll, szPath, ARRAYSIZE(szPath)))
  56. {
  57. dwVerInfoSize = GetFileVersionInfoSize(szPath, &dwVerHnd);
  58. if (dwVerInfoSize)
  59. {
  60. lpInfo = (LPSTR)GlobalAlloc(GPTR, dwVerInfoSize);
  61. if (lpInfo)
  62. {
  63. if (GetFileVersionInfo(szPath, dwVerHnd, dwVerInfoSize, lpInfo))
  64. {
  65. if (VerQueryValue(lpInfo, "\\VarFileInfo\\Translation", (LPVOID *)&lpwTrans, &uLen) &&
  66. uLen >= (2 * sizeof(WORD)))
  67. {
  68. // set up buffer for calls to VerQueryValue()
  69. wnsprintf(szGet, ARRAYSIZE(szGet), "\\StringFileInfo\\%04X%04X\\FileVersion", lpwTrans[0], lpwTrans[1]);
  70. if (VerQueryValue(lpInfo, szGet, (LPVOID *)&lpVersion, &uLen) && uLen)
  71. {
  72. while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
  73. {
  74. dwProdNum *= 10;
  75. dwProdNum += (*lpVersion++ - '0');
  76. }
  77. if (5 == dwProdNum)
  78. {
  79. if (('.' == *lpVersion) || (',' == *lpVersion))
  80. lpVersion++;
  81. while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
  82. {
  83. dwMajNum *= 10;
  84. dwMajNum += (*lpVersion++ - '0');
  85. }
  86. g_dwRicheditVer = (dwMajNum >= 30) ? 3 : 2;
  87. // @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
  88. if ((2 == g_dwRicheditVer) && (0 != *lpVersion))
  89. {
  90. // Check to see if we have turned on the magic key to disable this stuff
  91. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegRoot, 0, KEY_QUERY_VALUE, &hkey))
  92. {
  93. cb = sizeof(dw);
  94. RegQueryValueEx(hkey, c_szRegRTLRichEditHACK, 0, NULL, (LPBYTE)&dw, &cb);
  95. RegCloseKey(hkey);
  96. }
  97. // If we didn't find the key, or it is 0, then check to see if we are
  98. // dealing with those special richedits
  99. if (0 == dw)
  100. {
  101. if (('.' == *lpVersion) || (',' == *lpVersion))
  102. lpVersion++;
  103. while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
  104. {
  105. dwMinNum *= 10;
  106. dwMinNum += (*lpVersion++ - '0');
  107. }
  108. g_fSpecialRTLRichedit = (dwMinNum >= 330);
  109. }
  110. }
  111. }
  112. else
  113. {
  114. // Treat this as richedit version 3.0
  115. Assert(5 < dwProdNum);
  116. g_dwRicheditVer = 3;
  117. }
  118. fSucceeded = TRUE;
  119. }
  120. }
  121. }
  122. GlobalFree((HGLOBAL)lpInfo);
  123. }
  124. }
  125. }
  126. if (!fSucceeded)
  127. {
  128. FreeLibrary(g_hRichEditDll);
  129. g_hRichEditDll=NULL;
  130. }
  131. }
  132. }
  133. if(!g_hRichEditDll)
  134. {
  135. g_hRichEditDll=LoadLibrary("RICHED32.DLL");
  136. g_dwRicheditVer = 1;
  137. }
  138. if(!g_hRichEditDll)
  139. AthMessageBoxW(g_hwndInit, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrLoadingHtmlEdit), NULL, MB_OK);
  140. return (BOOL)(0 != g_hRichEditDll);
  141. }
  142. else
  143. {
  144. if(g_hRichEditDll)
  145. FreeLibrary(g_hRichEditDll);
  146. g_hRichEditDll=NULL;
  147. g_dwRicheditVer = 0;
  148. return TRUE;
  149. }
  150. }
  151. LPCSTR GetREClassStringA(void)
  152. {
  153. switch (g_dwRicheditVer)
  154. {
  155. case 1:
  156. return g_szRE10;
  157. case 2:
  158. case 3:
  159. return g_szRE20;
  160. default:
  161. AssertSz(FALSE, "Bad Richedit Version");
  162. return NULL;
  163. }
  164. }
  165. LPCWSTR GetREClassStringW(void)
  166. {
  167. switch (g_dwRicheditVer)
  168. {
  169. case 1:
  170. return g_wszRE10;
  171. case 2:
  172. case 3:
  173. return g_wszRE20;
  174. default:
  175. AssertSz(FALSE, "Bad Richedit Version");
  176. return NULL;
  177. }
  178. }
  179. typedef struct tagHWNDORDERSTRUCT {
  180. HWND hwndTemplate;
  181. HWND hwndRichEdit;
  182. HWND *phwnd;
  183. } HWNDORDERSTRUCT;
  184. BOOL CALLBACK CountChildrenProc(HWND hwnd, LPARAM lParam)
  185. {
  186. DWORD *pcCount = (DWORD*)lParam;
  187. (*pcCount)++;
  188. return TRUE;
  189. }
  190. // This function basically will take the order of the hwnds on the dialog
  191. // and store that order in the prder->phwnd variable so that we can maintain
  192. // the taborder on the dialog windows. The only special case we have to do
  193. // is with the template. In the case of the template, we need to substitute
  194. // in the richedit into the order.
  195. BOOL CALLBACK BuildOrderProc(HWND hwnd, LPARAM lParam)
  196. {
  197. HWNDORDERSTRUCT *pOrder = (HWNDORDERSTRUCT*)lParam;
  198. if (hwnd == pOrder->hwndTemplate)
  199. *pOrder->phwnd = pOrder->hwndRichEdit;
  200. else
  201. *pOrder->phwnd = hwnd;
  202. pOrder->phwnd++;
  203. return TRUE;
  204. }
  205. HWND CreateREInDialogA(HWND hwndParent, int iID)
  206. {
  207. HWND hwndTemplate = GetDlgItem(hwndParent, idredtTemplate);
  208. RECT rcTemplate;
  209. int width,
  210. height;
  211. HWND hwndNewRE = 0;
  212. DWORD cCount = 0;
  213. HWNDORDERSTRUCT hos;
  214. CHAR szTitle[CCHMAX_STRINGRES];
  215. Assert(IsWindow(hwndParent));
  216. Assert(iID);
  217. AssertSz(IsWindow(hwndTemplate), "Must have a template control for this to work.");
  218. *szTitle = 0;
  219. ShowWindow(hwndTemplate, SW_HIDE);
  220. GetWindowText(hwndTemplate, szTitle, ARRAYSIZE(szTitle));
  221. // Get location of place holder so we can create the richedit over it
  222. GetWindowRect(hwndTemplate, &rcTemplate);
  223. // Map Screen coordinates to dialog coordinates
  224. MapWindowPoints(NULL, hwndParent, (LPPOINT)&rcTemplate, 2);
  225. width = rcTemplate.right - rcTemplate.left;
  226. height = rcTemplate.bottom - rcTemplate.top;
  227. hwndNewRE = CreateWindow(GetREClassStringA(),
  228. szTitle,
  229. ES_MULTILINE|ES_READONLY|ES_SAVESEL|ES_AUTOVSCROLL|
  230. WS_BORDER|WS_VSCROLL|WS_TABSTOP|WS_CHILD,
  231. rcTemplate.left, rcTemplate.top, width, height,
  232. hwndParent,
  233. (HMENU)IntToPtr(iID),
  234. g_hInst, NULL);
  235. // Count number of child windows in the dialog
  236. if (EnumChildWindows(hwndParent, CountChildrenProc, (LPARAM)&cCount))
  237. {
  238. HWND *phwnd = (HWND*)ZeroAllocate(cCount*sizeof(HWND));
  239. if (phwnd)
  240. {
  241. hos.hwndRichEdit = hwndNewRE;
  242. hos.hwndTemplate = hwndTemplate;
  243. hos.phwnd = phwnd;
  244. // Create ordered list of dialog children hwnds.
  245. if (EnumChildWindows(hwndParent, BuildOrderProc, (LPARAM)&hos))
  246. {
  247. cCount--;
  248. // So this next section is basically going to reparent all the
  249. // controls in the order setup in the phwnd array. By setting
  250. // the parent of the window, we basically set the taborder of that
  251. // item to be the first in line. That is why we reparent the controls
  252. // in reverse order.
  253. HWND *pCurr = &phwnd[cCount];
  254. while(cCount)
  255. {
  256. SetParent(*pCurr, SetParent(*pCurr, NULL));
  257. pCurr--;
  258. cCount--;
  259. }
  260. }
  261. MemFree(phwnd);
  262. }
  263. }
  264. return hwndNewRE;
  265. }
  266. LONG RichEditNormalizeCharPos(HWND hwnd, LONG lByte, LPCSTR pszText)
  267. {
  268. LPWSTR pwszText = NULL;
  269. LPSTR pszConv = NULL;
  270. LONG cch;
  271. HRESULT hr = S_OK;
  272. //on anything other than richedit 1, we're already normalized
  273. if(1!=g_dwRicheditVer) return lByte;
  274. if(NULL == pszText)
  275. {
  276. cch = GetWindowTextLengthWrapW(hwnd);
  277. if (0 == cch)
  278. return 0;
  279. cch += 1; // for a null
  280. if (lByte >= cch)
  281. return cch;
  282. IF_NULLEXIT(MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)));
  283. *pwszText = '\0';
  284. GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
  285. IF_NULLEXIT((pszConv = PszToANSI(CP_ACP, pwszText)));
  286. pszText = pszConv;
  287. }
  288. cch = 0;
  289. while(lByte > 0) // lByte is zero-based
  290. {
  291. cch++;
  292. if (IsDBCSLeadByte(*pszText)){
  293. pszText++;
  294. lByte--;
  295. }
  296. pszText++;
  297. lByte--;
  298. }
  299. exit:
  300. MemFree(pwszText);
  301. MemFree(pszConv);
  302. return FAILED(hr)?0:cch;
  303. }
  304. LONG GetRichEditTextLen(HWND hwnd)
  305. {
  306. switch (g_dwRicheditVer)
  307. {
  308. // we always want to return the number of chars; riched 1 will always return
  309. // the number of bytes; we need to convert...
  310. case 1:
  311. {
  312. LPWSTR pwszText = NULL;
  313. DWORD cch;
  314. cch = GetWindowTextLengthWrapW(hwnd);
  315. if (0 == cch)
  316. return 0;
  317. cch += 1; // for a null
  318. if (!MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)))
  319. return 0;
  320. *pwszText = '\0';
  321. GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
  322. cch = lstrlenW(pwszText);
  323. MemFree(pwszText);
  324. return cch;
  325. }
  326. case 2:
  327. case 3:
  328. {
  329. GETTEXTLENGTHEX rTxtStruct;
  330. if (!hwnd)
  331. return 0;
  332. rTxtStruct.flags = GTL_DEFAULT;
  333. rTxtStruct.codepage = CP_UNICODE;
  334. return (LONG) SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&rTxtStruct, 0);
  335. }
  336. default:
  337. AssertSz(FALSE, "Bad Richedit Version");
  338. return 0;
  339. }
  340. }
  341. DWORD GetRichEditText(HWND hwnd, LPWSTR pwchBuff, DWORD cchNumChars, BOOL fSelection, ITextDocument *pDoc)
  342. {
  343. switch (g_dwRicheditVer)
  344. {
  345. case 1:
  346. {
  347. if (fSelection)
  348. return (DWORD)SendMessageWrapW(hwnd, EM_GETSELTEXT, 0, (LPARAM)pwchBuff);
  349. else
  350. return GetWindowTextWrapW(hwnd, pwchBuff, cchNumChars);
  351. }
  352. case 2:
  353. case 3:
  354. {
  355. DWORD cchLen = 0;
  356. if (!hwnd || !cchNumChars)
  357. return 0;
  358. Assert(pwchBuff);
  359. *pwchBuff = 0;
  360. if (!fSelection)
  361. {
  362. GETTEXTEX rTxtStruct;
  363. rTxtStruct.cb = cchNumChars * sizeof(WCHAR);
  364. rTxtStruct.flags = GT_DEFAULT;
  365. rTxtStruct.codepage = CP_UNICODE;
  366. cchLen = (DWORD) SendMessage(hwnd, EM_GETTEXTEX, (WPARAM)&rTxtStruct, (LPARAM)pwchBuff);
  367. }
  368. else
  369. {
  370. // There is no way to get a selection of UNICODE
  371. // without going through TOM
  372. ITextSelection *pSelRange = NULL;
  373. ITextDocument *pDocToFree = NULL;
  374. BSTR bstr = NULL;
  375. if (!pDoc)
  376. {
  377. LPRICHEDITOLE preole = NULL;
  378. LRESULT result = SendMessage(hwnd, EM_GETOLEINTERFACE, NULL, (LPARAM)&preole);
  379. Assert(result);
  380. Assert(preole);
  381. HRESULT hr = preole->QueryInterface(IID_ITextDocument, (LPVOID*)&pDocToFree);
  382. ReleaseObj(preole);
  383. TraceResult(hr);
  384. if (FAILED(hr))
  385. return 0;
  386. pDoc = pDocToFree;
  387. }
  388. if (SUCCEEDED(pDoc->GetSelection(&pSelRange)))
  389. {
  390. if (SUCCEEDED(pSelRange->GetText(&bstr)) && bstr)
  391. {
  392. cchLen = SysStringLen(bstr);
  393. if (cchLen > 0)
  394. {
  395. if (cchLen < cchNumChars)
  396. StrCpyNW(pwchBuff, bstr, cchNumChars);
  397. else
  398. cchLen = 0;
  399. }
  400. SysFreeString(bstr);
  401. }
  402. ReleaseObj(pSelRange);
  403. }
  404. ReleaseObj(pDocToFree);
  405. }
  406. return cchLen;
  407. }
  408. default:
  409. AssertSz(FALSE, "Bad Richedit Version");
  410. return 0;
  411. }
  412. }
  413. void SetRichEditText(HWND hwnd, LPWSTR pwchBuff, BOOL fReplaceSel, ITextDocument *pDoc, BOOL fReadOnly)
  414. {
  415. if (!hwnd)
  416. return;
  417. switch (g_dwRicheditVer)
  418. {
  419. case 1:
  420. {
  421. if (fReplaceSel)
  422. SendMessageWrapW(hwnd, EM_REPLACESEL, 0, (LPARAM)pwchBuff);
  423. else
  424. SetWindowTextWrapW(hwnd, pwchBuff);
  425. break;
  426. }
  427. case 2:
  428. {
  429. ITextServices *pService = NULL;
  430. ITextDocument *pDocToFree = NULL;
  431. if (!pDoc)
  432. {
  433. LPRICHEDITOLE preole = NULL;
  434. LRESULT result = SendMessage(hwnd, EM_GETOLEINTERFACE, NULL, (LPARAM)&preole);
  435. Assert(result);
  436. Assert(preole);
  437. HRESULT hr = preole->QueryInterface(IID_ITextDocument, (LPVOID*)&pDocToFree);
  438. ReleaseObj(preole);
  439. TraceResult(hr);
  440. if (FAILED(hr))
  441. return;
  442. pDoc = pDocToFree;
  443. }
  444. if (FAILED(pDoc->QueryInterface(IID_ITextServices, (LPVOID*)&pService)))
  445. return;
  446. if (!fReplaceSel)
  447. {
  448. // TxSetText is documented as a LPCTSTR, but RichEdit always
  449. // compiles with UNICODE turned on, so we are OK here.
  450. pService->TxSetText(pwchBuff);
  451. }
  452. else
  453. {
  454. HRESULT hr;
  455. ITextSelection *pSelRange = NULL;
  456. BSTR bstr = SysAllocString(pwchBuff);
  457. hr = pDoc->GetSelection(&pSelRange);
  458. if (SUCCEEDED(hr) && pSelRange)
  459. {
  460. if (fReadOnly)
  461. pService->OnTxPropertyBitsChange(TXTBIT_READONLY, 0);
  462. // If we are readonly, SetText will fail if we don't do the TXTBIT setting
  463. hr = pSelRange->SetText(bstr);
  464. if (FAILED(hr))
  465. TraceResult(hr);
  466. // Richedit 2.0 doesn't always collapse to the end of the selection.
  467. pSelRange->Collapse(tomEnd);
  468. if (fReadOnly)
  469. pService->OnTxPropertyBitsChange(TXTBIT_READONLY, TXTBIT_READONLY);
  470. pSelRange->Release();
  471. }
  472. else
  473. TraceResult(hr);
  474. SysFreeString(bstr);
  475. }
  476. ReleaseObj(pDocToFree);
  477. pService->Release();
  478. break;
  479. }
  480. case 3:
  481. {
  482. // pwchBuff can be NULL. NULL means that we are clearing the field.
  483. SETTEXTEX rTxtStruct;
  484. rTxtStruct.flags = fReplaceSel ? ST_SELECTION : ST_DEFAULT;
  485. rTxtStruct.codepage = CP_UNICODE;
  486. // EM_SETTEXTEX will fail with RichEdit 2.0.
  487. SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&rTxtStruct, (LPARAM)pwchBuff);
  488. break;
  489. }
  490. default:
  491. AssertSz(FALSE, "Bad Richedit Version");
  492. break;
  493. }
  494. }
  495. void SetFontOnRichEdit(HWND hwnd, HFONT hfont)
  496. {
  497. switch (g_dwRicheditVer)
  498. {
  499. case 1:
  500. case 3:
  501. {
  502. CHARFORMAT cf;
  503. if (SUCCEEDED(FontToCharformat(hfont, &cf)))
  504. {
  505. SideAssert(FALSE != SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM) 0, (LPARAM) &cf));
  506. }
  507. break;
  508. }
  509. case 2:
  510. SideAssert(FALSE != SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)));
  511. break;
  512. default:
  513. AssertSz(FALSE, "Bad Richedit Version");
  514. break;
  515. }
  516. }
  517. ///////////////////////////////////////////////////////////////////////////////
  518. // RichEditExSetSel
  519. //
  520. // Raid 79304. Rich Edit Control version 1.0 does not handle
  521. // the EM_EXSETSEL message correctly for text with DBCS characters. It sets
  522. // the selection range based on bytes on not double byte characters.
  523. ///////////////////////////////////////////////////////////////////////////////
  524. LRESULT RichEditExSetSel(HWND hwnd, CHARRANGE *pchrg)
  525. {
  526. // We only need to deal with this on richedit 1 or we aren't selecting entire text
  527. Assert(pchrg);
  528. if ((1 == g_dwRicheditVer) && (-1 != pchrg->cpMax))
  529. {
  530. LRESULT lResult = 0;
  531. LPWSTR pwszText = NULL;
  532. LPSTR pszConv = NULL, pszText = NULL;
  533. CHARRANGE chrg={0};
  534. CHARRANGE chrgIn;
  535. DWORD cch, cDBCSBefore = 0, cDBCSIn = 0;
  536. chrgIn = *pchrg;
  537. // Get the text string for this control
  538. cch = GetRichEditTextLen(hwnd) + 1;
  539. if (0 == cch)
  540. return lResult;
  541. if (!MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)))
  542. return lResult;
  543. *pwszText = '\0';
  544. // Richedit 1.0 will never have a pDoc, so don't pass one in
  545. GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
  546. pszConv = PszToANSI(CP_ACP, pwszText);
  547. pszText = pszConv;
  548. SafeMemFree(pwszText);
  549. if (!pszText)
  550. {
  551. Assert(0);
  552. return lResult;
  553. }
  554. // Compute the BYTE range based on DBCS characters
  555. if (chrgIn.cpMax >= chrgIn.cpMin)
  556. {
  557. chrgIn.cpMax -= chrgIn.cpMin;
  558. // Look for DBCS chars before selection
  559. while ((chrgIn.cpMin > 0) && *pszText)
  560. {
  561. if ( IsDBCSLeadByte(*pszText) )
  562. {
  563. cDBCSBefore++;
  564. pszText += 2;
  565. }
  566. else
  567. {
  568. pszText++;
  569. }
  570. chrgIn.cpMin--;
  571. }
  572. // Look for DBCS chars in selection
  573. while ((chrgIn.cpMax > 0) && *pszText)
  574. {
  575. if ( IsDBCSLeadByte(*pszText) )
  576. {
  577. cDBCSIn++;
  578. pszText += 2;
  579. }
  580. else
  581. {
  582. pszText++;
  583. }
  584. chrgIn.cpMax--;
  585. }
  586. chrg.cpMin = pchrg->cpMin + cDBCSBefore;
  587. chrg.cpMax = pchrg->cpMax + cDBCSBefore + cDBCSIn;
  588. }
  589. MemFree(pszConv);
  590. lResult = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&chrg);
  591. return lResult;
  592. }
  593. else
  594. return SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)pchrg);
  595. }
  596. LRESULT RichEditExGetSel(HWND hwnd, CHARRANGE *pchrg)
  597. {
  598. LRESULT lResult;
  599. LPWSTR pwszText = NULL;
  600. LPSTR pszText = NULL;
  601. LONG cch;
  602. HRESULT hr;
  603. Assert(pchrg);
  604. // We only need to deal with this on richedit 1
  605. if (1 == g_dwRicheditVer)
  606. {
  607. lResult = SendMessageA(hwnd, EM_EXGETSEL, 0, (LPARAM)pchrg);
  608. cch = GetWindowTextLengthWrapW(hwnd);
  609. if (0 == cch)
  610. goto exit;
  611. IF_NULLEXIT(MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)));
  612. *pwszText = '\0';
  613. GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
  614. IF_NULLEXIT((pszText = PszToANSI(CP_ACP, pwszText)));
  615. pchrg->cpMin = RichEditNormalizeCharPos(hwnd, pchrg->cpMin, pszText);
  616. pchrg->cpMax = RichEditNormalizeCharPos(hwnd, pchrg->cpMax, pszText);
  617. }
  618. else
  619. lResult = SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)pchrg);
  620. exit:
  621. MemFree(pszText);
  622. MemFree(pwszText);
  623. return lResult;
  624. }
  625. // ***************************
  626. // RichEditProtectENChange
  627. //
  628. // hwnd = richedit to be protected
  629. // pdwOldMask = when fProtect, will return the original mask for the richedit
  630. // when !fProtect, will be passing in the new mask for the richedit
  631. // The new value should be the one that was passed back when first called
  632. // fProtect = Are we starting the protection or ending it
  633. //
  634. // Notes:
  635. //
  636. // This function is to protect the possiblity of a re-entrant notificition
  637. // while processing an EN_CHANGE message within richedit. It turns out that
  638. // all richedits except for ver3 that shipped with Office 2k will protect against
  639. // this themselves.
  640. //
  641. // This call should always be used in pairs with the first passing TRUE and the
  642. // second passing in false. At this point, is only used to Protect the EN_CHANGE
  643. // notification that is sent to the richedit.
  644. //
  645. // BUGBUG: It might be beneficial at some future date to make the granularity
  646. // of these richedit issues more fine to handle cases like this where most
  647. // v3 richedits handle this correctly.
  648. void RichEditProtectENChange(HWND hwnd, DWORD *pdwOldMask, BOOL fProtect)
  649. {
  650. Assert(IsWindow(hwnd));
  651. Assert(pdwOldMask);
  652. if (3 == g_dwRicheditVer)
  653. {
  654. DWORD dwMask = *pdwOldMask;
  655. if (fProtect)
  656. {
  657. dwMask = (DWORD) SendMessage(hwnd, EM_GETEVENTMASK, 0, 0);
  658. SendMessage(hwnd, EM_SETEVENTMASK, 0, dwMask & (~ENM_CHANGE));
  659. *pdwOldMask = dwMask;
  660. }
  661. else
  662. SendMessage(hwnd, EM_SETEVENTMASK, 0, dwMask);
  663. }
  664. }
  665. // @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
  666. void RichEditRTLMirroring(HWND hwndHeader, BOOL fSubject, LONG *plExtendFlags, BOOL fPreRECreation)
  667. {
  668. if (!g_fSpecialRTLRichedit)
  669. {
  670. if (fPreRECreation)
  671. {
  672. // a-msadek; RichEdit have a lot of problems with mirroring
  673. // let's disable mirroring for this child and rather 'simulate' it
  674. if (IS_WINDOW_RTL_MIRRORED(hwndHeader))
  675. {
  676. DISABLE_LAYOUT_INHERITANCE(hwndHeader);
  677. *plExtendFlags |= WS_EX_LEFTSCROLLBAR |WS_EX_RIGHT;
  678. if (fSubject)
  679. {
  680. *plExtendFlags |= WS_EX_RTLREADING;
  681. }
  682. }
  683. }
  684. else
  685. {
  686. if (IS_WINDOW_RTL_MIRRORED(hwndHeader))
  687. {
  688. ENABLE_LAYOUT_INHERITANCE(hwndHeader);
  689. }
  690. }
  691. }
  692. }