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.

4266 lines
138 KiB

  1. /****************************************************************************\
  2. * editec.c - Edit controls rewrite. Version II of edit controls.
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Created: 24-Jul-88 davidds
  7. \****************************************************************************/
  8. /* Warning: The single line editcontrols contain internal styles and API which
  9. * are need to support comboboxes. They are defined in combcom.h/combcom.inc
  10. * and may be redefined or renumbered as needed.
  11. */
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. LOOKASIDE EditLookaside;
  15. ICH ECFindTabA(LPSTR lpstr, ICH cch);
  16. ICH ECFindTabW(LPWSTR lpstr, ICH cch);
  17. #define umin(a, b) ((unsigned)(a) < (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
  18. #define umax(a, b) ((unsigned)(a) > (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
  19. #define UNICODE_CARRIAGERETURN ((WCHAR)0x0d)
  20. #define UNICODE_LINEFEED ((WCHAR)0x0a)
  21. #define UNICODE_TAB ((WCHAR)0x09)
  22. // IME Menu IDs
  23. #define ID_IMEOPENCLOSE 10001
  24. #define ID_SOFTKBDOPENCLOSE 10002
  25. #define ID_RECONVERTSTRING 10003
  26. typedef struct {
  27. DWORD fDisableCut : 1;
  28. DWORD fDisablePaste : 1;
  29. DWORD fNeedSeparatorBeforeImeMenu : 1;
  30. DWORD fIME : 1;
  31. } EditMenuItemState;
  32. /***************************************************************************\
  33. * Handlers common to both single and multi line edit controls.
  34. /***************************************************************************/
  35. /***************************************************************************\
  36. * ECLock
  37. *
  38. * History:
  39. \***************************************************************************/
  40. PSTR ECLock(
  41. PED ped)
  42. {
  43. PSTR ptext = LOCALLOCK(ped->hText, ped->hInstance);
  44. ped->iLockLevel++;
  45. /*
  46. * If this is the first lock of the text and the text is encoded
  47. * decode the text.
  48. */
  49. //RIPMSG2(RIP_VERBOSE, "lock : %d '%10s'\n", ped->iLockLevel, ptext);
  50. if (ped->iLockLevel == 1 && ped->fEncoded) {
  51. /*
  52. * rtlrundecode can't handle zero length strings
  53. */
  54. if (ped->cch != 0) {
  55. STRING string;
  56. string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
  57. string.Buffer = ptext;
  58. RtlRunDecodeUnicodeString(ped->seed, (PUNICODE_STRING)&string);
  59. //RIPMSG1(RIP_VERBOSE, "Decoding: '%10s'\n", ptext);
  60. }
  61. ped->fEncoded = FALSE;
  62. }
  63. return ptext;
  64. }
  65. /***************************************************************************\
  66. * ECUnlock
  67. *
  68. * History:
  69. \***************************************************************************/
  70. void ECUnlock(
  71. PED ped)
  72. {
  73. /*
  74. * if we are removing the last lock on the text and the password
  75. * character is set then encode the text
  76. */
  77. //RIPMSG1(RIP_VERBOSE, "unlock: %d '%10s'\n", ped->iLockLevel, ped->ptext);
  78. if (ped->charPasswordChar && ped->iLockLevel == 1 && ped->cch != 0) {
  79. UNICODE_STRING string;
  80. string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
  81. string.Buffer = LOCALLOCK(ped->hText, ped->hInstance);
  82. RtlRunEncodeUnicodeString(&(ped->seed), &string);
  83. //RIPMSG1(RIP_VERBOSE, "Encoding: '%10s'\n", ped->ptext);
  84. ped->fEncoded = TRUE;
  85. LOCALUNLOCK(ped->hText, ped->hInstance);
  86. }
  87. LOCALUNLOCK(ped->hText, ped->hInstance);
  88. ped->iLockLevel--;
  89. }
  90. /***************************************************************************\
  91. *
  92. * GetActualNegA()
  93. * For a given strip of text, this function computes the negative A width
  94. * for the whole strip and returns the value as a postive number.
  95. * It also fills the NegAInfo structure with details about the postion
  96. * of this strip that results in this Negative A.
  97. *
  98. \***************************************************************************/
  99. UINT GetActualNegA(
  100. HDC hdc,
  101. PED ped,
  102. int x,
  103. LPSTR lpstring,
  104. ICH ichString,
  105. int nCount,
  106. LPSTRIPINFO NegAInfo)
  107. {
  108. int iCharCount, i;
  109. int iLeftmostPoint = x;
  110. PABC pABCwidthBuff;
  111. UINT wCharIndex;
  112. int xStartPoint = x;
  113. ABC abc;
  114. // To begin with, let us assume that there is no negative A width for
  115. // this strip and initialize accodingly.
  116. NegAInfo->XStartPos = x;
  117. NegAInfo->lpString = lpstring;
  118. NegAInfo->nCount = 0;
  119. NegAInfo->ichString = ichString;
  120. // If the current font is not a TrueType font, then there can not be any
  121. // negative A widths.
  122. if (!ped->fTrueType) {
  123. if(!ped->charOverhang) {
  124. return 0;
  125. } else {
  126. NegAInfo->nCount = min(nCount, (int)ped->wMaxNegAcharPos);
  127. return ped->charOverhang;
  128. }
  129. }
  130. // How many characters are to be considered for computing Negative A ?
  131. iCharCount = min(nCount, (int)ped->wMaxNegAcharPos);
  132. // Do we have the info on individual character's widths?
  133. if(!ped->charWidthBuffer) {
  134. // No! So, let us tell them to consider all the characters.
  135. NegAInfo->nCount = iCharCount;
  136. return(iCharCount * ped->aveCharWidth);
  137. }
  138. pABCwidthBuff = (PABC) ped->charWidthBuffer;
  139. if (ped->fAnsi) {
  140. for (i = 0; i < iCharCount; i++) {
  141. wCharIndex = (UINT)(*((unsigned char *)lpstring));
  142. if (*lpstring == VK_TAB) {
  143. // To play it safe, we assume that this tab results in a tab length of
  144. // 1 pixel because this is the minimum possible tab length.
  145. x++;
  146. } else {
  147. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  148. x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width.
  149. else {
  150. GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc) ;
  151. x += abc.abcA;
  152. }
  153. if (x < iLeftmostPoint)
  154. iLeftmostPoint = x; // Reset the leftmost point.
  155. if (x < xStartPoint)
  156. NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1.
  157. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) {
  158. x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC;
  159. } else {
  160. x += abc.abcB + abc.abcC;
  161. }
  162. }
  163. lpstring++;
  164. }
  165. } else { // Unicode
  166. LPWSTR lpwstring = (LPWSTR) lpstring ;
  167. for (i = 0; i < iCharCount; i++) {
  168. wCharIndex = *lpwstring ;
  169. if (*lpwstring == VK_TAB) {
  170. // To play it safe, we assume that this tab results in a tab length of
  171. // 1 pixel because this is the minimum possible tab length.
  172. x++;
  173. } else {
  174. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  175. x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width.
  176. else {
  177. GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ;
  178. x += abc.abcA ;
  179. }
  180. if (x < iLeftmostPoint)
  181. iLeftmostPoint = x; // Reset the leftmost point.
  182. if (x < xStartPoint)
  183. NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1.
  184. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  185. x += pABCwidthBuff[wCharIndex].abcB +
  186. pABCwidthBuff[wCharIndex].abcC;
  187. else
  188. x += abc.abcB + abc.abcC ;
  189. }
  190. lpwstring++;
  191. }
  192. }
  193. // Let us return the negative A for the whole strip as a positive value.
  194. return((UINT)(xStartPoint - iLeftmostPoint));
  195. }
  196. /***************************************************************************\
  197. *
  198. * ECIsAncestorActive()
  199. *
  200. * Returns whether or not we're the child of an "active" window. Looks for
  201. * the first parent window that has a caption.
  202. *
  203. * This is a function because we might use it elsewhere when getting left
  204. * clicked on, etc.
  205. *
  206. \***************************************************************************/
  207. BOOL ECIsAncestorActive(HWND hwnd)
  208. {
  209. // We want to return TRUE always for top level windows. That's because
  210. // of how WM_MOUSEACTIVATE works. If we see the click at all, the
  211. // window is active. However, if we reach a child ancestor that has
  212. // a caption, return the frame-on style bit.
  213. //
  214. // Note that calling FlashWindow() will have an effect. If the user
  215. // clicks on an edit field in a child window that is flashed off, nothing
  216. // will happen unless the window stops flashing and ncactivates first.
  217. while (hwnd) {
  218. PWND pwnd = ValidateHwnd( hwnd );
  219. //
  220. // Bail out if some parent window isn't 4.0 compatible or we've
  221. // reached the top. Fixes compatibility problems with 3.x apps,
  222. // especially MFC samples.
  223. //
  224. if (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WFCHILD))
  225. hwnd = NULL; // to break us out of the loop
  226. else if (TestWF(pwnd, WFCPRESENT))
  227. return(TestWF(pwnd, WFFRAMEON) != 0);
  228. else
  229. hwnd = GetParent(hwnd);
  230. }
  231. return(TRUE);
  232. }
  233. /***************************************************************************\
  234. * ECSetIMEMenu()
  235. *
  236. * support IME specific context menu
  237. *
  238. * Create: 30-Apr-97 Hiroyama : Ported from Memphis
  239. \***************************************************************************/
  240. BOOL ECSetIMEMenu(
  241. HMENU hMenu,
  242. HWND hwnd,
  243. EditMenuItemState state)
  244. {
  245. MENUITEMINFO mii;
  246. HIMC hIMC;
  247. HKL hKL;
  248. HMENU hmenuSub;
  249. WCHAR szRes[32];
  250. int nPrevLastItem;
  251. int nItemsAdded = 0;
  252. UserAssert(IS_IME_ENABLED() && state.fIME);
  253. hKL = THREAD_HKL();
  254. if (!fpImmIsIME(hKL))
  255. return TRUE;
  256. hIMC = fpImmGetContext(hwnd);
  257. if (hIMC == NULL) {
  258. // early out
  259. return FALSE;
  260. }
  261. hmenuSub = GetSubMenu(hMenu, 0);
  262. if (hmenuSub == NULL) {
  263. return FALSE;
  264. }
  265. nPrevLastItem = GetMenuItemCount(hmenuSub);
  266. if (hIMC) {
  267. if (LOWORD(HandleToUlong(hKL)) != 0x412) {
  268. //
  269. // If Korean, do not show open/close menus
  270. //
  271. if (fpImmGetOpenStatus(hIMC))
  272. LoadString(hmodUser, STR_IMECLOSE, szRes, ARRAYSIZE(szRes));
  273. else
  274. LoadString(hmodUser, STR_IMEOPEN, szRes, ARRAYSIZE(szRes));
  275. mii.cbSize = sizeof(MENUITEMINFO);
  276. mii.fMask = MIIM_STRING | MIIM_ID;
  277. mii.dwTypeData = szRes;
  278. mii.cch = 0xffff;
  279. mii.wID = ID_IMEOPENCLOSE;
  280. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  281. ++nItemsAdded;
  282. }
  283. if (fpImmGetProperty(hKL, IGP_CONVERSION) & IME_CMODE_SOFTKBD) {
  284. DWORD fdwConversion;
  285. fpImmGetConversionStatus(hIMC, &fdwConversion, NULL);
  286. if (fdwConversion & IME_CMODE_SOFTKBD)
  287. LoadString(hmodUser, STR_SOFTKBDCLOSE, szRes, ARRAYSIZE(szRes));
  288. else
  289. LoadString(hmodUser, STR_SOFTKBDOPEN, szRes, ARRAYSIZE(szRes));
  290. mii.cbSize = sizeof(MENUITEMINFO);
  291. mii.fMask = MIIM_STRING | MIIM_ID;
  292. mii.dwTypeData = szRes;
  293. mii.cch = 0xffff;
  294. mii.wID = ID_SOFTKBDOPENCLOSE;
  295. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  296. ++nItemsAdded;
  297. }
  298. if (LOWORD(HandleToUlong(hKL)) != 0x412) {
  299. //
  300. // If Korean, do not show reconversion menus
  301. //
  302. DWORD dwSCS = fpImmGetProperty(hKL, IGP_SETCOMPSTR);
  303. LoadString(hmodUser, STR_RECONVERTSTRING, szRes, ARRAYSIZE(szRes));
  304. mii.cbSize = sizeof(MENUITEMINFO);
  305. mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE;
  306. mii.dwTypeData = szRes;
  307. mii.fState = 0;
  308. mii.cch = 0xffff;
  309. mii.wID = ID_RECONVERTSTRING;
  310. if (state.fDisableCut ||
  311. !(dwSCS & SCS_CAP_SETRECONVERTSTRING) ||
  312. !(dwSCS & SCS_CAP_MAKEREAD)) {
  313. mii.fState |= MFS_GRAYED;
  314. }
  315. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  316. ++nItemsAdded;
  317. }
  318. }
  319. //
  320. // Add or remove the menu separator
  321. //
  322. if (state.fNeedSeparatorBeforeImeMenu && nItemsAdded != 0) {
  323. //
  324. // If the menu for Middle East has left a separator,
  325. // fNeedSeparatorBeforeImeMenu is FALSE.
  326. // I.e. we don't need to add more.
  327. //
  328. mii.cbSize = sizeof(MENUITEMINFO);
  329. mii.fMask = MIIM_FTYPE;
  330. mii.fType = MFT_SEPARATOR;
  331. InsertMenuItem(hmenuSub, nPrevLastItem, TRUE, &mii);
  332. }
  333. else if (!state.fNeedSeparatorBeforeImeMenu && nItemsAdded == 0) {
  334. //
  335. // Extra separator is left by ME menus. Remove it.
  336. //
  337. UserVerify(NtUserDeleteMenu(hmenuSub, nPrevLastItem - 1, MF_BYPOSITION));
  338. }
  339. fpImmReleaseContext(hwnd, hIMC);
  340. return TRUE;
  341. }
  342. void ECInOutReconversionMode(PED ped, BOOL fIn)
  343. {
  344. UserAssert(fIn == TRUE || fIn == FALSE);
  345. if (fIn == ped->fInReconversion) {
  346. return;
  347. }
  348. ped->fInReconversion = fIn;
  349. if (ped->fFocus) {
  350. (fIn ? NtUserHideCaret: NtUserShowCaret)(ped->hwnd);
  351. }
  352. return;
  353. }
  354. /***************************************************************************\
  355. * ECDoIMEMenuCommand()
  356. *
  357. * support IME specific context menu
  358. *
  359. * Create: 30-Apr-97 Hiroyama : Ported from Memphis
  360. \***************************************************************************/
  361. BOOL NEAR ECDoIMEMenuCommand(PED ped, int cmd, HWND hwnd)
  362. {
  363. HIMC hIMC;
  364. // early out
  365. switch (cmd) {
  366. case ID_IMEOPENCLOSE:
  367. case ID_SOFTKBDOPENCLOSE:
  368. case ID_RECONVERTSTRING:
  369. break;
  370. default:
  371. return FALSE;
  372. }
  373. // everybody needs hIMC, so get it here
  374. hIMC = fpImmGetContext(hwnd);
  375. if (hIMC == NULL) {
  376. // indicate to caller, that no further command processing needed
  377. return TRUE;
  378. }
  379. switch (cmd) {
  380. case ID_IMEOPENCLOSE:
  381. {
  382. // switch IME Open/Close status
  383. BOOL fOpen = fpImmGetOpenStatus(hIMC);
  384. fpImmSetOpenStatus(hIMC, !fOpen);
  385. }
  386. break;
  387. case ID_SOFTKBDOPENCLOSE:
  388. {
  389. DWORD fdwConversion;
  390. if (fpImmGetConversionStatus(hIMC, &fdwConversion, NULL)) {
  391. //
  392. // Toggle soft keyboard Open/Close status
  393. //
  394. fpImmEnumInputContext(0, SyncSoftKbdState,
  395. (fdwConversion & IME_CMODE_SOFTKBD) != IME_CMODE_SOFTKBD);
  396. }
  397. }
  398. break;
  399. case ID_RECONVERTSTRING:
  400. {
  401. DWORD dwStrLen; // holds TCHAR count of recionversion string
  402. DWORD cbLen; // holds BYTE SIZE of reconversion string
  403. DWORD dwSize;
  404. LPRECONVERTSTRING lpRCS;
  405. // pass current selection to IME for reconversion
  406. dwStrLen = ped->ichMaxSel - ped->ichMinSel;
  407. cbLen = dwStrLen * ped->cbChar;
  408. dwSize = cbLen + sizeof(RECONVERTSTRING) + 8;
  409. lpRCS = (LPRECONVERTSTRING)UserLocalAlloc(0, dwSize);
  410. if (lpRCS) {
  411. LPBYTE pText;
  412. ICH ichSelMinOrg;
  413. ichSelMinOrg = ped->ichMinSel;
  414. pText = ECLock(ped);
  415. if (pText != NULL) {
  416. LPBYTE lpDest;
  417. BOOL (WINAPI* fpSetCompositionStringAW)(HIMC, DWORD, LPCVOID, DWORD, LPCVOID, DWORD);
  418. lpRCS->dwSize = dwSize;
  419. lpRCS->dwVersion = 0;
  420. lpRCS->dwStrLen =
  421. lpRCS->dwCompStrLen =
  422. lpRCS->dwTargetStrLen = dwStrLen;
  423. lpRCS->dwStrOffset = sizeof(RECONVERTSTRING);
  424. lpRCS->dwCompStrOffset =
  425. lpRCS->dwTargetStrOffset = 0;
  426. lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING);
  427. RtlCopyMemory(lpDest, pText + ped->ichMinSel * ped->cbChar, cbLen);
  428. if (ped->fAnsi) {
  429. LPBYTE psz = (LPBYTE)lpDest;
  430. psz[cbLen] = '\0';
  431. fpSetCompositionStringAW = fpImmSetCompositionStringA;
  432. } else {
  433. LPWSTR pwsz = (LPWSTR)lpDest;
  434. pwsz[dwStrLen] = L'\0';
  435. fpSetCompositionStringAW = fpImmSetCompositionStringW;
  436. }
  437. ECUnlock(ped);
  438. UserAssert(fpSetCompositionStringAW != NULL);
  439. ECInOutReconversionMode(ped, TRUE);
  440. ECImmSetCompositionWindow(ped, 0, 0); // x and y will be overriden anyway
  441. // Query the IME for a valid Reconvert string range first.
  442. fpSetCompositionStringAW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
  443. // If current IME updates the original reconvert structure,
  444. // it is necessary to update the text selection based on the
  445. // new reconvert text range.
  446. if ((lpRCS->dwCompStrLen != dwStrLen) || (ichSelMinOrg != ped->ichMinSel)) {
  447. ICH ichSelStart;
  448. ICH ichSelEnd;
  449. ichSelStart = ichSelMinOrg + (lpRCS->dwCompStrOffset / ped->cbChar);
  450. ichSelEnd = ichSelStart + lpRCS->dwCompStrLen;
  451. (ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd);
  452. }
  453. fpSetCompositionStringAW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
  454. } // pText
  455. UserLocalFree(lpRCS);
  456. }
  457. }
  458. break;
  459. default:
  460. // should never reach here.
  461. RIPMSG1(RIP_ERROR, "ECDoIMEMenuCommand: unknown command id %d; should never reach here.", cmd);
  462. return FALSE;
  463. }
  464. UserAssert(hIMC != NULL);
  465. fpImmReleaseContext(hwnd, hIMC);
  466. return TRUE;
  467. }
  468. /***************************************************************************\
  469. *
  470. * ECMenu()
  471. *
  472. * Handles context menu for edit fields. Disables inappropriate commands.
  473. * Note that this is NOT subclassing friendly, like most of our functions,
  474. * for speed and convenience.
  475. *
  476. \***************************************************************************/
  477. void ECMenu(
  478. HWND hwnd,
  479. PED ped,
  480. LPPOINT pt)
  481. {
  482. HMENU hMenu;
  483. int cmd = 0;
  484. int x;
  485. int y;
  486. UINT uFlags = TPM_NONOTIFY | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON;
  487. EditMenuItemState state = {
  488. FALSE, // fDisableCut
  489. TRUE, // fDisablePaste
  490. TRUE, // fNeedSeparatorBeforeImeMenu
  491. IS_IME_ENABLED() && fpImmIsIME(THREAD_HKL()), // fIME
  492. };
  493. // Set focus if we don't have it.
  494. if (!ped->fFocus)
  495. NtUserSetFocus(hwnd);
  496. // Grab the menu from USER's resources...
  497. if (!(hMenu = LoadMenu( hmodUser, MAKEINTRESOURCE( ID_EC_PROPERTY_MENU ))))
  498. return ;
  499. // Undo -- not allowed if we have no saved undo info
  500. if (ped->undoType == UNDO_NONE)
  501. EnableMenuItem(hMenu, WM_UNDO, MF_BYCOMMAND | MFS_GRAYED);
  502. if (ped->fReadOnly || ped->charPasswordChar) {
  503. // Cut and Delete -- not allowed if read-only or password
  504. state.fDisableCut = TRUE;
  505. } else {
  506. // Cut, Delete -- not allowed if there's no selection
  507. if (ped->ichMinSel == ped->ichMaxSel)
  508. state.fDisableCut = TRUE;
  509. }
  510. // Paste -- not allowed if there's no text on the clipboard
  511. // (this works for both OEM and Unicode)
  512. // Used to be always disabled for password edits MCostea #221035
  513. if (NtUserIsClipboardFormatAvailable(CF_TEXT))
  514. state.fDisablePaste = FALSE;
  515. if (state.fDisableCut) {
  516. EnableMenuItem(hMenu, WM_CUT, MF_BYCOMMAND | MFS_GRAYED);
  517. EnableMenuItem(hMenu, WM_CLEAR, MF_BYCOMMAND | MFS_GRAYED);
  518. }
  519. if (state.fDisablePaste)
  520. EnableMenuItem(hMenu, WM_PASTE, MF_BYCOMMAND | MFS_GRAYED);
  521. // Copy -- not allowed if there's no selection or password ec
  522. if ((ped->ichMinSel == ped->ichMaxSel) || (ped->charPasswordChar))
  523. EnableMenuItem(hMenu, WM_COPY, MF_BYCOMMAND | MFS_GRAYED);
  524. // Select All -- not allowed if there's no text or if everything is
  525. // selected. Latter case takes care of first one.
  526. if ((ped->ichMinSel == 0) && (ped->ichMaxSel == ped->cch))
  527. EnableMenuItem(hMenu, EM_SETSEL, MF_BYCOMMAND | MFS_GRAYED);
  528. if (ped->pLpkEditCallout) {
  529. ped->pLpkEditCallout->EditSetMenu(ped, hMenu);
  530. } else {
  531. NtUserDeleteMenu(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND);
  532. NtUserDeleteMenu(hMenu, ID_CNTX_RTL, MF_BYCOMMAND);
  533. NtUserDeleteMenu(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND);
  534. if (state.fIME) {
  535. // One separator is left in the menu,
  536. // no need to add the one before IME menus
  537. state.fNeedSeparatorBeforeImeMenu = FALSE;
  538. } else {
  539. // Extra separator is left. Remove it.
  540. HMENU hmenuSub = GetSubMenu(hMenu, 0);
  541. int nItems = GetMenuItemCount(hmenuSub) - 1;
  542. UserAssert(nItems >= 0);
  543. UserAssert(GetMenuState(hmenuSub, nItems, MF_BYPOSITION) & MF_SEPARATOR);
  544. // remove needless separator
  545. UserVerify(NtUserDeleteMenu(hmenuSub, nItems, MF_BYPOSITION));
  546. }
  547. }
  548. // IME specific menu
  549. if (state.fIME) {
  550. ECSetIMEMenu(hMenu, hwnd, state);
  551. }
  552. // BOGUS
  553. // We position the menu below & to the right of the point clicked on.
  554. // Is this cool? I think so. Excel 4.0 does the same thing. It
  555. // seems like it would be neat if we could avoid obscuring the
  556. // selection. But in actuality, it seems even more awkward to move
  557. // the menu out of the way of the selection. The user can't click
  558. // and drag that way, and they have to move the mouse a ton.
  559. //
  560. // We need to use TPM_NONOTIFY because VBRUN100 and VBRUN200 GP-fault
  561. // on unexpected menu messages.
  562. //
  563. /*
  564. * if message came via the keyboard then center on the control
  565. * We use -1 && -1 here not 0xFFFFFFFF like Win95 becuase we
  566. * previously converted the lParam to a point with sign extending.
  567. */
  568. if (pt->x == -1 && pt->y == -1) {
  569. RECT rc;
  570. GetWindowRect(hwnd, &rc);
  571. x = rc.left + (rc.right - rc.left) / 2;
  572. y = rc.top + (rc.bottom - rc.top) / 2;
  573. } else {
  574. x = pt->x;
  575. y = pt->y;
  576. }
  577. if (RTL_UI()) {
  578. uFlags |= TPM_LAYOUTRTL;
  579. }
  580. cmd = NtUserTrackPopupMenuEx(GetSubMenu(hMenu, 0), uFlags, x, y, hwnd, NULL);
  581. // Free our menu
  582. NtUserDestroyMenu(hMenu);
  583. if (cmd && (cmd != -1)) {
  584. if (ped->pLpkEditCallout && cmd) {
  585. ped->pLpkEditCallout->EditProcessMenu(ped, cmd);
  586. }
  587. if (!state.fIME || !ECDoIMEMenuCommand(ped, cmd, hwnd)) {
  588. // if cmd is not IME specific menu, send it.
  589. SendMessage(hwnd, cmd, 0, (cmd == EM_SETSEL) ? 0xFFFFFFFF : 0L );
  590. }
  591. }
  592. }
  593. /***************************************************************************\
  594. *
  595. * ECClearText()
  596. *
  597. * Clears selected text. Does NOT _send_ a fake char backspace.
  598. *
  599. \***************************************************************************/
  600. void ECClearText(PED ped) {
  601. if (!ped->fReadOnly &&
  602. (ped->ichMinSel < ped->ichMaxSel)) {
  603. if (ped->fSingle)
  604. SLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L );
  605. else
  606. MLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L );
  607. }
  608. }
  609. /***************************************************************************\
  610. *
  611. * ECCutText() -
  612. *
  613. * Cuts selected text. This removes and copies the selection to the clip,
  614. * or if nothing is selected we delete (clear) the left character.
  615. *
  616. \***************************************************************************/
  617. void ECCutText(PED ped) {
  618. // Cut selection--IE, remove and copy to clipboard, or if no selection,
  619. // delete (clear) character left.
  620. if (!ped->fReadOnly &&
  621. (ped->ichMinSel < ped->ichMaxSel) &&
  622. SendMessage(ped->hwnd, WM_COPY, 0, 0L)) {
  623. // If copy was successful, delete the copied text by sending a
  624. // backspace message which will redraw the text and take care of
  625. // notifying the parent of changes.
  626. ECClearText(ped);
  627. }
  628. }
  629. /***************************************************************************\
  630. *
  631. * ECGetModKeys()
  632. *
  633. * Gets modifier key states. Currently, we only check for VK_CONTROL and
  634. * VK_SHIFT.
  635. *
  636. \***************************************************************************/
  637. int ECGetModKeys(int keyMods) {
  638. int scState;
  639. scState = 0;
  640. if (!keyMods) {
  641. if (GetKeyState(VK_CONTROL) < 0)
  642. scState |= CTRLDOWN;
  643. if (GetKeyState(VK_SHIFT) < 0)
  644. scState |= SHFTDOWN;
  645. } else if (keyMods != NOMODIFY)
  646. scState = keyMods;
  647. return scState;
  648. }
  649. /***************************************************************************\
  650. *
  651. * ECTabTheTextOut() AorW
  652. * If fDrawText == FALSE, then this function returns the text extent of
  653. * of the given strip of text. It does not worry about the Negative widths.
  654. *
  655. * If fDrawText == TRUE, this draws the given strip of Text expanding the
  656. * tabs to proper lengths, calculates and fills up the NegCInfoForStrip with
  657. * details required to draw the portions of this strip that goes beyond the
  658. * xClipEndPos due to Negative C widths.
  659. *
  660. * Returns the max width AS A DWORD. We don't care about the height
  661. * at all. No one uses it. We keep a DWORD because that way we avoid
  662. * overflow.
  663. *
  664. * NOTE: If the language pack is loaded EcTabTheTextOut is not used - the
  665. * language pack must take care of all tab expansion and selection
  666. * highlighting with full support for bidi layout and complex script
  667. * glyph reordering.
  668. *
  669. \***************************************************************************/
  670. UINT ECTabTheTextOut(
  671. HDC hdc,
  672. int xClipStPos,
  673. int xClipEndPos,
  674. int xStart,
  675. int y,
  676. LPSTR lpstring,
  677. int nCount,
  678. ICH ichString,
  679. PED ped,
  680. int iTabOrigin,
  681. BOOL fDraw,
  682. LPSTRIPINFO NegCInfoForStrip)
  683. {
  684. int nTabPositions; // Count of tabstops in tabstop array.
  685. LPINT lpintTabStopPositions; // Tab stop positions in pixels.
  686. int cch;
  687. UINT textextent;
  688. int xEnd;
  689. int pixeltabstop = 0;
  690. int i;
  691. int cxCharWidth;
  692. RECT rc;
  693. BOOL fOpaque;
  694. BOOL fFirstPass = TRUE;
  695. PINT charWidthBuff;
  696. int iTabLength;
  697. int nConsecutiveTabs;
  698. int xStripStPos;
  699. int xStripEndPos;
  700. int xEndOfStrip;
  701. STRIPINFO RedrawStripInfo;
  702. STRIPINFO NegAInfo;
  703. LPSTR lpTab;
  704. LPWSTR lpwTab;
  705. UINT wNegCwidth, wNegAwidth;
  706. int xRightmostPoint = xClipStPos;
  707. int xTabStartPos;
  708. int iSavedBkMode = 0;
  709. WCHAR wchar;
  710. SIZE size;
  711. ABC abc ;
  712. // Algorithm: Draw the strip opaquely first. If a tab length is so
  713. // small that the portions of text on either side of a tab overlap with
  714. // the other, then this will result in some clipping. So, such portion
  715. // of the strip is remembered in "RedrawStripInfo" and redrawn
  716. // transparently later to compensate the clippings.
  717. // NOTE: "RedrawStripInfo" can hold info about just one portion. So, if
  718. // more than one portion of the strip needs to be redrawn transparently,
  719. // then we "merge" all such portions into a single strip and redraw that
  720. // strip at the end.
  721. if (fDraw) {
  722. // To begin with, let us assume that there is no Negative C for this
  723. // strip and initialize the Negative Width Info structure.
  724. NegCInfoForStrip->nCount = 0;
  725. NegCInfoForStrip->XStartPos = xClipEndPos;
  726. // We may not have to redraw any portion of this strip.
  727. RedrawStripInfo.nCount = 0;
  728. fOpaque = (GetBkMode(hdc) == OPAQUE) || (fDraw == ECT_SELECTED);
  729. }
  730. #if DBG
  731. else {
  732. //
  733. // Both MLGetLineWidth() and ECCchInWidth() should be clipping
  734. // nCount to avoid overflow.
  735. //
  736. if (nCount > MAXLINELENGTH)
  737. RIPMSG0(RIP_WARNING, "ECTabTheTextOut: nCount > MAXLINELENGTH");
  738. }
  739. #endif
  740. // Let us define the Clip rectangle.
  741. rc.left = xClipStPos;
  742. rc.right = xClipEndPos;
  743. rc.top = y;
  744. rc.bottom = y + ped->lineHeight;
  745. // Check if anything needs to be drawn.
  746. if (!lpstring || !nCount) {
  747. if (fDraw)
  748. ExtTextOutW(hdc, xClipStPos, y,
  749. (fOpaque ? ETO_OPAQUE | ETO_CLIPPED : ETO_CLIPPED),
  750. &rc, L"", 0, 0L);
  751. return(0L);
  752. }
  753. //
  754. // Starting position
  755. //
  756. xEnd = xStart;
  757. cxCharWidth = ped->aveCharWidth;
  758. nTabPositions = (ped->pTabStops ? *(ped->pTabStops) : 0);
  759. if (ped->pTabStops) {
  760. lpintTabStopPositions = (LPINT)(ped->pTabStops+1);
  761. if (nTabPositions == 1) {
  762. pixeltabstop = lpintTabStopPositions[0];
  763. if (!pixeltabstop)
  764. pixeltabstop = 1;
  765. }
  766. } else {
  767. lpintTabStopPositions = NULL;
  768. pixeltabstop = 8*cxCharWidth;
  769. }
  770. // The first time we will draw the strip Opaquely. If some portions need
  771. // to be redrawn , then we will set the mode to TRANSPARENT and
  772. // jump to this location to redraw those portions.
  773. RedrawStrip:
  774. while (nCount) {
  775. wNegCwidth = ped->wMaxNegC;
  776. // Search for the first TAB in this strip; also compute the extent
  777. // of the the strip upto and not including the tab character.
  778. //
  779. // Note - If the langpack is loaded, there will be no charWidthBuffer.
  780. //
  781. if (ped->charWidthBuffer) { // Do we have a character width buffer?
  782. textextent = 0;
  783. cch = nCount;
  784. if (ped->fTrueType) { // If so, does it have ABC widths?
  785. UINT iRightmostPoint = 0;
  786. UINT wCharIndex;
  787. PABC pABCwidthBuff;
  788. pABCwidthBuff = (PABC) ped->charWidthBuffer;
  789. if ( ped->fAnsi ) {
  790. for (i = 0; i < nCount; i++) {
  791. if (lpstring[i] == VK_TAB) {
  792. cch = i;
  793. break;
  794. }
  795. wCharIndex = (UINT)(((unsigned char *)lpstring)[i]);
  796. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) {
  797. textextent += (UINT)(pABCwidthBuff[wCharIndex].abcA +
  798. pABCwidthBuff[wCharIndex].abcB);
  799. } else { // not in cache, will ask driver
  800. GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc);
  801. textextent += abc.abcA + abc.abcB ;
  802. }
  803. if (textextent > iRightmostPoint)
  804. iRightmostPoint = textextent;
  805. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) {
  806. textextent += pABCwidthBuff[wCharIndex].abcC;
  807. } else { // not in cache
  808. textextent += abc.abcC;
  809. }
  810. if (textextent > iRightmostPoint)
  811. iRightmostPoint = textextent;
  812. }
  813. } else { // Unicode
  814. for (i = 0; i < nCount; i++) {
  815. WCHAR UNALIGNED * lpwstring = (WCHAR UNALIGNED *)lpstring;
  816. if (lpwstring[i] == VK_TAB) {
  817. cch = i;
  818. break;
  819. }
  820. wCharIndex = lpwstring[i] ;
  821. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  822. textextent += pABCwidthBuff[wCharIndex].abcA +
  823. pABCwidthBuff[wCharIndex].abcB;
  824. else {
  825. GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ;
  826. textextent += abc.abcA + abc.abcB ;
  827. }
  828. /*
  829. * Note that abcC could be negative so we need this
  830. * statement here *and* below
  831. */
  832. if (textextent > iRightmostPoint)
  833. iRightmostPoint = textextent;
  834. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  835. textextent += pABCwidthBuff[wCharIndex].abcC;
  836. else
  837. textextent += abc.abcC ;
  838. if (textextent > iRightmostPoint)
  839. iRightmostPoint = textextent;
  840. }
  841. }
  842. wNegCwidth = (int)(iRightmostPoint - textextent);
  843. } else { // !ped->fTrueType
  844. // No! This is not a TrueType font; So, we have only character
  845. // width info in this buffer.
  846. charWidthBuff = ped->charWidthBuffer;
  847. if ( ped->fAnsi ) {
  848. // Initially assume no tabs exist in the text so cch=nCount.
  849. for (i = 0; i < nCount; i++) {
  850. if (lpstring[i] == VK_TAB) {
  851. cch = i;
  852. break;
  853. }
  854. //
  855. // Call GetTextExtentPoint for dbcs/hankaku characters
  856. //
  857. if (ped->fDBCS && (i+1 < nCount)
  858. && ECIsDBCSLeadByte(ped,lpstring[i])) {
  859. GetTextExtentPointA(hdc, &lpstring[i], 2, &size);
  860. textextent += size.cx;
  861. i++;
  862. } else if ((UCHAR)lpstring[i] >= CHAR_WIDTH_BUFFER_LENGTH) {
  863. // Skip this GetExtentPoint call for non hankaku code points
  864. // Or if the character is in the width cache.
  865. GetTextExtentPointA(hdc, &lpstring[i], 1, &size);
  866. textextent += size.cx;
  867. } else {
  868. textextent += (UINT)(charWidthBuff[(UINT)(((unsigned char *)lpstring)[i])]);
  869. }
  870. }
  871. } else {
  872. LPWSTR lpwstring = (LPWSTR) lpstring ;
  873. INT cchUStart; // start of unicode character count
  874. for (i = 0; i < nCount; i++) {
  875. if (lpwstring[i] == VK_TAB) {
  876. cch = i;
  877. break;
  878. }
  879. wchar = lpwstring[i];
  880. if (wchar >= CHAR_WIDTH_BUFFER_LENGTH) {
  881. /*
  882. * We have a Unicode character that is not in our
  883. * cache, get all the characters outside the cache
  884. * before getting the text extent on this part of the
  885. * string.
  886. */
  887. cchUStart = i;
  888. while (wchar >= CHAR_WIDTH_BUFFER_LENGTH &&
  889. wchar != VK_TAB && i < nCount) {
  890. wchar = lpwstring[++i];
  891. }
  892. GetTextExtentPointW(hdc, (LPWSTR)lpwstring + cchUStart,
  893. i-cchUStart, &size);
  894. textextent += size.cx;
  895. if (wchar == VK_TAB || i >= nCount) {
  896. cch = i;
  897. break;
  898. }
  899. /*
  900. * We have a char that is in the cache, fall through.
  901. */
  902. }
  903. /*
  904. * The width of this character is in the cache buffer.
  905. */
  906. textextent += ped->charWidthBuffer[wchar];
  907. }
  908. }
  909. } // fTrueType else.
  910. nCount -= cch;
  911. } else { // If we don't have a buffer that contains the width info.
  912. /*
  913. * Gotta call the driver to do our text extent.
  914. */
  915. if ( ped->fAnsi ) {
  916. cch = (int)ECFindTabA(lpstring, nCount);
  917. GetTextExtentPointA(hdc, lpstring, cch, &size) ;
  918. } else {
  919. cch = (int)ECFindTabW((LPWSTR) lpstring, nCount);
  920. GetTextExtentPointW(hdc, (LPWSTR)lpstring, cch, &size);
  921. }
  922. nCount -= cch;
  923. //
  924. // Subtruct Overhang for Italic fonts.
  925. //
  926. textextent = (size.cx - ped->charOverhang);
  927. }
  928. //
  929. // textextent is computed.
  930. //
  931. xStripStPos = xEnd;
  932. xEnd += (int)textextent;
  933. xStripEndPos = xEnd;
  934. // We will consider the negative widths only if when we draw opaquely.
  935. if (fFirstPass && fDraw) {
  936. xRightmostPoint = max(xStripEndPos + (int)wNegCwidth, xRightmostPoint);
  937. // Check if this strip peeps beyond the clip region.
  938. if (xRightmostPoint > xClipEndPos) {
  939. if (!NegCInfoForStrip->nCount) {
  940. NegCInfoForStrip->lpString = lpstring;
  941. NegCInfoForStrip->ichString = ichString;
  942. NegCInfoForStrip->nCount = nCount+cch;
  943. NegCInfoForStrip->XStartPos = xStripStPos;
  944. }
  945. }
  946. } /* if (fFirstPass && fDraw) */
  947. if ( ped->fAnsi )
  948. lpTab = lpstring + cch; // Possibly Points to a tab character.
  949. else
  950. lpwTab = ((LPWSTR)lpstring) + cch ;
  951. // we must consider all the consecutive tabs and calculate the
  952. // the begining of next strip.
  953. nConsecutiveTabs = 0;
  954. while (nCount &&
  955. (ped->fAnsi ? (*lpTab == VK_TAB) : (*lpwTab == VK_TAB))) {
  956. // Find the next tab position and update the x value.
  957. xTabStartPos = xEnd;
  958. if (pixeltabstop)
  959. xEnd = (((xEnd-iTabOrigin)/pixeltabstop)*pixeltabstop) +
  960. pixeltabstop + iTabOrigin;
  961. else {
  962. for (i = 0; i < nTabPositions; i++) {
  963. if (xEnd < (lpintTabStopPositions[i] + iTabOrigin)) {
  964. xEnd = (lpintTabStopPositions[i] + iTabOrigin);
  965. break;
  966. }
  967. }
  968. // Check if all the tabstops set are exhausted; Then start using
  969. // default tab stop positions.
  970. if (i == nTabPositions) {
  971. pixeltabstop = 8*cxCharWidth;
  972. xEnd = ((xEnd - iTabOrigin)/pixeltabstop)*pixeltabstop +
  973. pixeltabstop + iTabOrigin;
  974. }
  975. }
  976. if (fFirstPass && fDraw) {
  977. xRightmostPoint = max(xEnd, xRightmostPoint);
  978. /* Check if this strip peeps beyond the clip region */
  979. if (xRightmostPoint > xClipEndPos) {
  980. if (!NegCInfoForStrip->nCount) {
  981. NegCInfoForStrip->ichString = ichString + cch + nConsecutiveTabs;
  982. NegCInfoForStrip->nCount = nCount;
  983. NegCInfoForStrip->lpString = (ped->fAnsi ?
  984. lpTab : (LPSTR) lpwTab);
  985. NegCInfoForStrip->XStartPos = xTabStartPos;
  986. }
  987. }
  988. } /* if(fFirstPass) */
  989. nConsecutiveTabs++;
  990. nCount--;
  991. ped->fAnsi ? lpTab++ : (LPSTR) (lpwTab++) ; // Move to the next character.
  992. } // while(*lpTab == TAB) //
  993. if (fDraw) {
  994. if (fFirstPass) {
  995. // Is anything remaining to be drawn in this strip?
  996. if (!nCount)
  997. rc.right = xEnd; // No! We are done.
  998. else {
  999. // "x" is the effective starting position of next strip.
  1000. iTabLength = xEnd - xStripEndPos;
  1001. // Check if there is a possibility of this tab length being too small
  1002. // compared to the negative A and C widths if any.
  1003. if ((wNegCwidth + (wNegAwidth = ped->wMaxNegA)) > (UINT)iTabLength) {
  1004. // Unfortunately, there is a possiblity of an overlap.
  1005. // Let us find out the actual NegA for the next strip.
  1006. wNegAwidth = GetActualNegA(
  1007. hdc,
  1008. ped,
  1009. xEnd,
  1010. lpstring + (cch + nConsecutiveTabs)*ped->cbChar,
  1011. ichString + cch + nConsecutiveTabs,
  1012. nCount,
  1013. &NegAInfo);
  1014. }
  1015. // Check if they actually overlap //
  1016. if ((wNegCwidth + wNegAwidth) <= (UINT)iTabLength) {
  1017. // No overlap between the strips. This is the ideal situation.
  1018. rc.right = xEnd - wNegAwidth;
  1019. } else {
  1020. // Yes! They overlap.
  1021. rc.right = xEnd;
  1022. // See if negative C width is too large compared to tab length.
  1023. if (wNegCwidth > (UINT)iTabLength) {
  1024. // Must redraw transparently a part of the current strip later.
  1025. if (RedrawStripInfo.nCount) {
  1026. // A previous strip also needs to be redrawn; So, merge this
  1027. // strip to that strip.
  1028. RedrawStripInfo.nCount = (ichString -
  1029. RedrawStripInfo.ichString) + cch;
  1030. } else {
  1031. RedrawStripInfo.nCount = cch;
  1032. RedrawStripInfo.lpString = lpstring;
  1033. RedrawStripInfo.ichString = ichString;
  1034. RedrawStripInfo.XStartPos = xStripStPos;
  1035. }
  1036. }
  1037. if (wNegAwidth) {
  1038. // Must redraw transparently the first part of the next strip later.
  1039. if (RedrawStripInfo.nCount) {
  1040. // A previous strip also needs to be redrawn; So, merge this
  1041. // strip to that strip.
  1042. RedrawStripInfo.nCount = (NegAInfo.ichString - RedrawStripInfo.ichString) +
  1043. NegAInfo.nCount;
  1044. } else
  1045. RedrawStripInfo = NegAInfo;
  1046. }
  1047. }
  1048. } // else (!nCount) //
  1049. } // if (fFirstPass) //
  1050. if (rc.left < xClipEndPos) {
  1051. if (fFirstPass) {
  1052. // If this is the end of the strip, then complete the rectangle.
  1053. if ((!nCount) && (xClipEndPos == MAXCLIPENDPOS))
  1054. rc.right = max(rc.right, xClipEndPos);
  1055. else
  1056. rc.right = min(rc.right, xClipEndPos);
  1057. }
  1058. // Draw the current strip.
  1059. if (rc.left < rc.right)
  1060. if ( ped->fAnsi )
  1061. ExtTextOutA(hdc,
  1062. xStripStPos,
  1063. y,
  1064. (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
  1065. (LPRECT)&rc, lpstring, cch, 0L);
  1066. else
  1067. ExtTextOutW(hdc,
  1068. xStripStPos,
  1069. y,
  1070. (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
  1071. (LPRECT)&rc, (LPWSTR)lpstring, cch, 0L);
  1072. }
  1073. if (fFirstPass)
  1074. rc.left = max(rc.right, xClipStPos);
  1075. ichString += (cch+nConsecutiveTabs);
  1076. } // if (fDraw) //
  1077. // Skip over the tab and the characters we just drew.
  1078. lpstring += (cch + nConsecutiveTabs) * ped->cbChar;
  1079. } // while (nCount) //
  1080. xEndOfStrip = xEnd;
  1081. // check if we need to draw some portions transparently.
  1082. if (fFirstPass && fDraw && RedrawStripInfo.nCount) {
  1083. iSavedBkMode = SetBkMode(hdc, TRANSPARENT);
  1084. fFirstPass = FALSE;
  1085. nCount = RedrawStripInfo.nCount;
  1086. rc.left = xClipStPos;
  1087. rc.right = xClipEndPos;
  1088. lpstring = RedrawStripInfo.lpString;
  1089. ichString = RedrawStripInfo.ichString;
  1090. xEnd = RedrawStripInfo.XStartPos;
  1091. goto RedrawStrip; // Redraw Transparently.
  1092. }
  1093. if (iSavedBkMode) // Did we change the Bk mode?
  1094. SetBkMode(hdc, iSavedBkMode); // Then, let us set it back!
  1095. return((UINT)(xEndOfStrip - xStart));
  1096. }
  1097. /***************************************************************************\
  1098. * ECCchInWidth AorW
  1099. *
  1100. * Returns maximum count of characters (up to cch) from the given
  1101. * string (starting either at the beginning and moving forward or at the
  1102. * end and moving backwards based on the setting of the fForward flag)
  1103. * which will fit in the given width. ie. Will tell you how much of
  1104. * lpstring will fit in the given width even when using proportional
  1105. * characters. WARNING: If we use kerning, then this loses...
  1106. *
  1107. * History:
  1108. *
  1109. * NOTE: ECCchInWidth is not called if the language pack is loaded.
  1110. \***************************************************************************/
  1111. ICH ECCchInWidth(
  1112. PED ped,
  1113. HDC hdc,
  1114. LPSTR lpText,
  1115. ICH cch,
  1116. int width,
  1117. BOOL fForward)
  1118. {
  1119. int stringExtent;
  1120. int cchhigh;
  1121. int cchnew = 0;
  1122. int cchlow = 0;
  1123. SIZE size;
  1124. LPSTR lpStart;
  1125. if ((width <= 0) || !cch)
  1126. return (0);
  1127. /*
  1128. * Optimize nonproportional fonts for single line ec since they don't have
  1129. * tabs.
  1130. */
  1131. //
  1132. // Change optimize condition for fixed pitch font
  1133. //
  1134. if (ped->fNonPropFont && ped->fSingle && !ped->fDBCS) {
  1135. return (ECAdjustIch( ped, lpText, umin(width/ped->aveCharWidth,(int)cch)));
  1136. }
  1137. /*
  1138. * Check if password hidden chars are being used.
  1139. */
  1140. if (ped->charPasswordChar) {
  1141. return (umin(width / ped->cPasswordCharWidth, (int)cch));
  1142. }
  1143. /*
  1144. * ALWAYS RESTRICT TO AT MOST MAXLINELENGTH to avoid overflow...
  1145. */
  1146. cch = umin(MAXLINELENGTH, cch);
  1147. cchhigh = cch + 1;
  1148. while (cchlow < cchhigh - 1) {
  1149. cchnew = umax((cchhigh - cchlow) / 2, 1) + cchlow;
  1150. lpStart = lpText;
  1151. /*
  1152. * If we want to figure out how many fit starting at the end and moving
  1153. * backwards, make sure we move to the appropriate position in the
  1154. * string before calculating the text extent.
  1155. */
  1156. if (!fForward)
  1157. lpStart += (cch - cchnew)*ped->cbChar;
  1158. if (ped->fSingle) {
  1159. if (ped->fAnsi)
  1160. GetTextExtentPointA(hdc, (LPSTR)lpStart, cchnew, &size);
  1161. else
  1162. GetTextExtentPointW(hdc, (LPWSTR)lpStart, cchnew, &size);
  1163. stringExtent = size.cx;
  1164. } else {
  1165. stringExtent = ECTabTheTextOut(hdc, 0, 0, 0, 0,
  1166. lpStart,
  1167. cchnew, 0,
  1168. ped, 0, ECT_CALC, NULL );
  1169. }
  1170. if (stringExtent > width) {
  1171. cchhigh = cchnew;
  1172. } else {
  1173. cchlow = cchnew;
  1174. }
  1175. }
  1176. //
  1177. // Call ECAdjustIch ( generic case )
  1178. //
  1179. cchlow = ECAdjustIch( ped, lpText, cchlow );
  1180. return (cchlow);
  1181. }
  1182. /***************************************************************************\
  1183. * ECFindTab
  1184. *
  1185. * Scans lpstr and return s the number of CHARs till the first TAB.
  1186. * Scans at most cch chars of lpstr.
  1187. *
  1188. * History:
  1189. \***************************************************************************/
  1190. ICH ECFindTabA(
  1191. LPSTR lpstr,
  1192. ICH cch)
  1193. {
  1194. LPSTR copylpstr = lpstr;
  1195. if (!cch)
  1196. return 0;
  1197. while (*lpstr != VK_TAB) {
  1198. lpstr++;
  1199. if (--cch == 0)
  1200. break;
  1201. }
  1202. return ((ICH)(lpstr - copylpstr));
  1203. }
  1204. ICH ECFindTabW(
  1205. LPWSTR lpstr,
  1206. ICH cch)
  1207. {
  1208. LPWSTR copylpstr = lpstr;
  1209. if (!cch)
  1210. return 0;
  1211. while (*lpstr != VK_TAB) {
  1212. lpstr++;
  1213. if (--cch == 0)
  1214. break;
  1215. }
  1216. return ((ICH)(lpstr - copylpstr));
  1217. }
  1218. /***************************************************************************\
  1219. *
  1220. * ECGetBrush()
  1221. *
  1222. * Gets appropriate background brush to erase with.
  1223. *
  1224. \***************************************************************************/
  1225. HBRUSH ECGetBrush(PED ped, HDC hdc)
  1226. {
  1227. HBRUSH hbr;
  1228. BOOL f40Compat;
  1229. f40Compat = (GETAPPVER() >= VER40);
  1230. // Get background brush
  1231. if ((ped->fReadOnly || ped->fDisabled) && f40Compat) {
  1232. hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLORSTATIC);
  1233. } else
  1234. hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLOREDIT);
  1235. if (ped->fDisabled && (ped->fSingle || f40Compat)) {
  1236. DWORD rgb;
  1237. // Change text color
  1238. rgb = GetSysColor(COLOR_GRAYTEXT);
  1239. if (rgb != GetBkColor(hdc))
  1240. SetTextColor(hdc, rgb);
  1241. }
  1242. return(hbr);
  1243. }
  1244. /***************************************************************************\
  1245. * NextWordCallBack
  1246. *
  1247. *
  1248. *
  1249. * History:
  1250. * 02-19-92 JimA Ported from Win31 sources.
  1251. \***************************************************************************/
  1252. void NextWordCallBack(
  1253. PED ped,
  1254. ICH ichStart,
  1255. BOOL fLeft,
  1256. ICH *pichMin,
  1257. ICH *pichMax )
  1258. {
  1259. ICH ichMinSel;
  1260. ICH ichMaxSel;
  1261. LPSTR pText;
  1262. pText = ECLock(ped);
  1263. if (fLeft || (!(BOOL)CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText,
  1264. ichStart, ped->cch, WB_ISDELIMITER) &&
  1265. (ped->fAnsi ? (*(pText + ichStart) != VK_RETURN) : (*((LPWSTR)pText + ichStart) != VK_RETURN))
  1266. ))
  1267. ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_LEFT);
  1268. else
  1269. ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_RIGHT);
  1270. ichMaxSel = min(ichMinSel + 1, ped->cch);
  1271. if (ped->fAnsi) {
  1272. if (*(pText + ichMinSel) == VK_RETURN) {
  1273. if (ichMinSel > 0 && *(pText + ichMinSel - 1) == VK_RETURN) {
  1274. /*
  1275. * So that we can treat CRCRLF as one word also.
  1276. */
  1277. ichMinSel--;
  1278. } else if (*(pText+ichMinSel + 1) == VK_RETURN) {
  1279. /*
  1280. * Move MaxSel on to the LF
  1281. */
  1282. ichMaxSel++;
  1283. }
  1284. }
  1285. } else {
  1286. if (*((LPWSTR)pText + ichMinSel) == VK_RETURN) {
  1287. if (ichMinSel > 0 && *((LPWSTR)pText + ichMinSel - 1) == VK_RETURN) {
  1288. /*
  1289. * So that we can treat CRCRLF as one word also.
  1290. */
  1291. ichMinSel--;
  1292. } else if (*((LPWSTR)pText+ichMinSel + 1) == VK_RETURN) {
  1293. /*
  1294. * Move MaxSel on to the LF
  1295. */
  1296. ichMaxSel++;
  1297. }
  1298. }
  1299. }
  1300. ichMaxSel = CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichMaxSel, ped->cch, WB_RIGHT);
  1301. ECUnlock(ped);
  1302. if (pichMin) *pichMin = ichMinSel;
  1303. if (pichMax) *pichMax = ichMaxSel;
  1304. }
  1305. /***************************************************************************\
  1306. * NextWordLpkCallback
  1307. *
  1308. * Identifies next/prev word position for complex scripts
  1309. *
  1310. * History:
  1311. * 04-22-97 DBrown
  1312. \***************************************************************************/
  1313. void NextWordLpkCallBack(
  1314. PED ped,
  1315. ICH ichStart,
  1316. BOOL fLeft,
  1317. ICH *pichMin,
  1318. ICH *pichMax)
  1319. {
  1320. PSTR pText = ECLock(ped);
  1321. HDC hdc = ECGetEditDC(ped, TRUE);
  1322. ped->pLpkEditCallout->EditNextWord(ped, hdc, pText, ichStart, fLeft, pichMin, pichMax);
  1323. ECReleaseEditDC(ped, hdc, TRUE);
  1324. ECUnlock(ped);
  1325. }
  1326. /***************************************************************************\
  1327. * ECWordAorW
  1328. *
  1329. * if fLeft, Returns the ichMinSel and ichMaxSel of the word to the
  1330. * left of ichStart. ichMinSel contains the starting letter of the word,
  1331. * ichmaxsel contains all spaces up to the first character of the next word.
  1332. *
  1333. * if !fLeft, Returns the ichMinSel and ichMaxSel of the word to the right of
  1334. * ichStart. ichMinSel contains the starting letter of the word, ichmaxsel
  1335. * contains the first letter of the next word. If ichStart is in the middle
  1336. * of a word, that word is considered the left or right word.
  1337. *
  1338. * A CR LF pair or CRCRLF triple is considered a single word in
  1339. * multiline edit controls.
  1340. *
  1341. * History:
  1342. \***************************************************************************/
  1343. void ECWord(
  1344. PED ped,
  1345. ICH ichStart,
  1346. BOOL fLeft,
  1347. ICH *pichMin,
  1348. ICH *pichMax )
  1349. {
  1350. BOOL charLocated = FALSE;
  1351. BOOL spaceLocated = FALSE;
  1352. if ((!ichStart && fLeft) || (ichStart == ped->cch && !fLeft)) {
  1353. /*
  1354. * We are at the beginning of the text (looking left) or we are at end
  1355. * of text (looking right), no word here
  1356. */
  1357. if (pichMin) *pichMin=0;
  1358. if (pichMax) *pichMax=0;
  1359. return;
  1360. }
  1361. /*
  1362. * Don't give out hints about word breaks if password chars are being used,
  1363. */
  1364. if (ped->charPasswordChar) {
  1365. if (pichMin) *pichMin=0;
  1366. if (pichMax) *pichMax=ped->cch;
  1367. return;
  1368. }
  1369. if (ped->fAnsi) {
  1370. PSTR pText;
  1371. PSTR pWordMinSel;
  1372. PSTR pWordMaxSel;
  1373. PSTR pPrevChar;
  1374. UserAssert(ped->cbChar == sizeof(CHAR));
  1375. if (ped->lpfnNextWord) {
  1376. NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1377. return;
  1378. }
  1379. if (ped->pLpkEditCallout) {
  1380. NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1381. return;
  1382. }
  1383. pText = ECLock(ped);
  1384. pWordMinSel = pWordMaxSel = pText + ichStart;
  1385. /*
  1386. * if fLeft: Move pWordMinSel to the left looking for the start of a word.
  1387. * If we start at a space, we will include spaces in the selection as we
  1388. * move left untill we find a nonspace character. At that point, we continue
  1389. * looking left until we find a space. Thus, the selection will consist of
  1390. * a word with its trailing spaces or, it will consist of any leading at the
  1391. * beginning of a line of text.
  1392. */
  1393. /*
  1394. * if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
  1395. * word. If the pWordMinSel points to a character, then we move left
  1396. * looking for a space which will signify the start of the word. If
  1397. * pWordMinSel points to a space, we look right till we come upon a
  1398. * character. pMaxWord will look right starting at pMinWord looking for the
  1399. * end of the word and its trailing spaces.
  1400. */
  1401. if (fLeft || !ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0D) {
  1402. /*
  1403. * If we are moving left or if we are moving right and we are not on a
  1404. * space or a CR (the start of a word), then we was look left for the
  1405. * start of a word which is either a CR or a character. We do this by
  1406. * looking left till we find a character (or if CR we stop), then we
  1407. * continue looking left till we find a space or LF.
  1408. */
  1409. while (pWordMinSel > pText && ((!ISDELIMETERA(*(pWordMinSel - 1)) &&
  1410. *(pWordMinSel - 1) != 0x0A) || !charLocated)) {
  1411. /*
  1412. * Treat double byte character as a word ( in ansi pWordMinSel loop )
  1413. */
  1414. pPrevChar = ECAnsiPrev( ped, pText, pWordMinSel );
  1415. /*
  1416. ** we are looking right ( !fLeft ).
  1417. ** if current character is a double byte chararacter or
  1418. ** previous character is a double byte character, we
  1419. ** are on the beggining of a word.
  1420. */
  1421. if ( !fLeft && ( ISDELIMETERA( *pPrevChar ) ||
  1422. *pPrevChar == 0x0A ||
  1423. ECIsDBCSLeadByte(ped, *pWordMinSel) ||
  1424. pWordMinSel - pPrevChar == 2 ) ) {
  1425. /*
  1426. * If we are looking for the start of the word right, then we
  1427. * stop when we have found it. (needed in case charLocated is
  1428. * still FALSE)
  1429. */
  1430. break;
  1431. }
  1432. if ( pWordMinSel - pPrevChar == 2 ) {
  1433. /*
  1434. ** previous character is a double byte character.
  1435. ** if we are in a word ( charLocated == TRUE )
  1436. ** current position is the beginning of the word
  1437. ** if we are not in a word ( charLocated == FALSE )
  1438. ** the previous character is what we looking for.
  1439. */
  1440. if ( ! charLocated ) {
  1441. pWordMinSel = pPrevChar;
  1442. }
  1443. break;
  1444. }
  1445. pWordMinSel = pPrevChar;
  1446. if (!ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0A) {
  1447. /*
  1448. * We have found the last char in the word. Continue looking
  1449. * backwards till we find the first char of the word
  1450. */
  1451. charLocated = TRUE;
  1452. /*
  1453. * We will consider a CR the start of a word
  1454. */
  1455. if (*pWordMinSel == 0x0D)
  1456. break;
  1457. }
  1458. }
  1459. } else {
  1460. while ((ISDELIMETERA(*pWordMinSel) || *pWordMinSel == 0x0A) && pWordMinSel < pText + ped->cch)
  1461. pWordMinSel++;
  1462. }
  1463. /*
  1464. * Adjust the initial position of pWordMaxSel ( in ansi )
  1465. */
  1466. pWordMaxSel = ECAnsiNext(ped, pWordMinSel);
  1467. pWordMaxSel = min(pWordMaxSel, pText + ped->cch);
  1468. /*
  1469. ** If pWordMinSel points a double byte character AND
  1470. ** pWordMaxSel points non space
  1471. ** then
  1472. ** pWordMaxSel points the beggining of next word.
  1473. */
  1474. if ( ( pWordMaxSel - pWordMinSel == 2 ) && ! ISDELIMETERA(*pWordMaxSel) )
  1475. goto FastReturnA;
  1476. if (*pWordMinSel == 0x0D) {
  1477. if (pWordMinSel > pText && *(pWordMinSel - 1) == 0x0D)
  1478. /* So that we can treat CRCRLF as one word also. */
  1479. pWordMinSel--;
  1480. else if (*(pWordMinSel + 1) == 0x0D)
  1481. /* Move MaxSel on to the LF */
  1482. pWordMaxSel++;
  1483. }
  1484. /*
  1485. * Check if we have a one character word
  1486. */
  1487. if (ISDELIMETERA(*pWordMaxSel))
  1488. spaceLocated = TRUE;
  1489. /*
  1490. * Move pWordMaxSel to the right looking for the end of a word and its
  1491. * trailing spaces. WordMaxSel stops on the first character of the next
  1492. * word. Thus, we break either at a CR or at the first nonspace char after
  1493. * a run of spaces or LFs.
  1494. */
  1495. while ((pWordMaxSel < pText + ped->cch) && (!spaceLocated || (ISDELIMETERA(*pWordMaxSel)))) {
  1496. if (*pWordMaxSel == 0x0D)
  1497. break;
  1498. /*
  1499. * Treat double byte character as a word ( in ansi pWordMaxSel loop )
  1500. */
  1501. /*
  1502. ** if it's a double byte character then
  1503. ** we are at the beginning of next word
  1504. ** which is a double byte character.
  1505. */
  1506. if (ECIsDBCSLeadByte( ped, *pWordMaxSel))
  1507. break;
  1508. pWordMaxSel++;
  1509. if (ISDELIMETERA(*pWordMaxSel))
  1510. spaceLocated = TRUE;
  1511. if (*(pWordMaxSel - 1) == 0x0A)
  1512. break;
  1513. }
  1514. /*
  1515. * label for fast return ( for Ansi )
  1516. */
  1517. FastReturnA:
  1518. ECUnlock(ped);
  1519. if (pichMin) *pichMin = (ICH)(pWordMinSel - pText);
  1520. if (pichMax) *pichMax = (ICH)(pWordMaxSel - pText);
  1521. return;
  1522. } else { // !fAnsi
  1523. LPWSTR pwText;
  1524. LPWSTR pwWordMinSel;
  1525. LPWSTR pwWordMaxSel;
  1526. BOOL charLocated = FALSE;
  1527. BOOL spaceLocated = FALSE;
  1528. PWSTR pwPrevChar;
  1529. UserAssert(ped->cbChar == sizeof(WCHAR));
  1530. if (ped->lpfnNextWord) {
  1531. NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1532. return;
  1533. }
  1534. if (ped->pLpkEditCallout) {
  1535. NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1536. return;
  1537. }
  1538. pwText = (LPWSTR)ECLock(ped);
  1539. pwWordMinSel = pwWordMaxSel = pwText + ichStart;
  1540. /*
  1541. * if fLeft: Move pWordMinSel to the left looking for the start of a word.
  1542. * If we start at a space, we will include spaces in the selection as we
  1543. * move left untill we find a nonspace character. At that point, we continue
  1544. * looking left until we find a space. Thus, the selection will consist of
  1545. * a word with its trailing spaces or, it will consist of any leading at the
  1546. * beginning of a line of text.
  1547. */
  1548. /*
  1549. * if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
  1550. * word. If the pWordMinSel points to a character, then we move left
  1551. * looking for a space which will signify the start of the word. If
  1552. * pWordMinSel points to a space, we look right till we come upon a
  1553. * character. pMaxWord will look right starting at pMinWord looking for the
  1554. * end of the word and its trailing spaces.
  1555. */
  1556. if (fLeft || (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0D))
  1557. /* If we are moving left or if we are moving right and we are not on a
  1558. * space or a CR (the start of a word), then we was look left for the
  1559. * start of a word which is either a CR or a character. We do this by
  1560. * looking left till we find a character (or if CR we stop), then we
  1561. * continue looking left till we find a space or LF.
  1562. */ {
  1563. while (pwWordMinSel > pwText && ((!ISDELIMETERW(*(pwWordMinSel - 1)) && *(pwWordMinSel - 1) != 0x0A) || !charLocated)) {
  1564. /*
  1565. * Treat double byte character as a word ( in unicode pwWordMinSel loop )
  1566. */
  1567. pwPrevChar = pwWordMinSel - 1;
  1568. /*
  1569. ** we are looking right ( !fLeft ).
  1570. **
  1571. ** if current character is a double width chararacter
  1572. ** or previous character is a double width character,
  1573. ** we are on the beggining of a word.
  1574. */
  1575. if (!fLeft && (ISDELIMETERW( *pwPrevChar) ||
  1576. *pwPrevChar == 0x0A ||
  1577. UserIsFullWidth(CP_ACP,*pwWordMinSel) ||
  1578. UserIsFullWidth(CP_ACP,*pwPrevChar))) {
  1579. /*
  1580. * If we are looking for the start of the word right, then we
  1581. * stop when we have found it. (needed in case charLocated is
  1582. * still FALSE)
  1583. */
  1584. break;
  1585. }
  1586. if (UserIsFullWidth(CP_ACP,*pwPrevChar)) {
  1587. /*
  1588. ** Previous character is a double width character.
  1589. **
  1590. ** if we are in a word ( charLocated == TRUE )
  1591. ** current position is the beginning of the word
  1592. ** if we are not in a word ( charLocated == FALSE )
  1593. ** the previous character is what we looking for.
  1594. */
  1595. if ( ! charLocated ) {
  1596. pwWordMinSel = pwPrevChar;
  1597. }
  1598. break;
  1599. }
  1600. pwWordMinSel = pwPrevChar;
  1601. if (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0A)
  1602. /*
  1603. * We have found the last char in the word. Continue looking
  1604. * backwards till we find the first char of the word
  1605. */ {
  1606. charLocated = TRUE;
  1607. /*
  1608. * We will consider a CR the start of a word
  1609. */
  1610. if (*pwWordMinSel == 0x0D)
  1611. break;
  1612. }
  1613. }
  1614. } else {
  1615. /*
  1616. * We are moving right and we are in between words so we need to move
  1617. * right till we find the start of a word (either a CR or a character.
  1618. */
  1619. while ((ISDELIMETERW(*pwWordMinSel) || *pwWordMinSel == 0x0A) && pwWordMinSel < pwText + ped->cch)
  1620. pwWordMinSel++;
  1621. }
  1622. pwWordMaxSel = min((pwWordMinSel + 1), (pwText + ped->cch));
  1623. /*
  1624. ** If pwWordMinSel points a double width character AND
  1625. ** pwWordMaxSel points non space
  1626. ** then
  1627. ** pwWordMaxSel points the beggining of next word.
  1628. */
  1629. if (UserIsFullWidth(CP_ACP,*pwWordMinSel) && ! ISDELIMETERW(*pwWordMaxSel))
  1630. goto FastReturnW;
  1631. if (*pwWordMinSel == 0x0D) {
  1632. if (pwWordMinSel > pwText && *(pwWordMinSel - 1) == 0x0D)
  1633. /* So that we can treat CRCRLF as one word also. */
  1634. pwWordMinSel--;
  1635. else if (*(pwWordMinSel + 1) == 0x0D)
  1636. /* Move MaxSel on to the LF */
  1637. pwWordMaxSel++;
  1638. }
  1639. /*
  1640. * Check if we have a one character word
  1641. */
  1642. if (ISDELIMETERW(*pwWordMaxSel))
  1643. spaceLocated = TRUE;
  1644. /*
  1645. * Move pwWordMaxSel to the right looking for the end of a word and its
  1646. * trailing spaces. WordMaxSel stops on the first character of the next
  1647. * word. Thus, we break either at a CR or at the first nonspace char after
  1648. * a run of spaces or LFs.
  1649. */
  1650. while ((pwWordMaxSel < pwText + ped->cch) && (!spaceLocated || (ISDELIMETERW(*pwWordMaxSel)))) {
  1651. if (*pwWordMaxSel == 0x0D)
  1652. break;
  1653. /*
  1654. * treat double byte character as a word ( in unicode pwWordMaxSel loop )
  1655. */
  1656. /*
  1657. ** if it's a double width character
  1658. ** then we are at the beginning of
  1659. ** the next word which is a double
  1660. ** width character.
  1661. */
  1662. if (UserIsFullWidth(CP_ACP,*pwWordMaxSel))
  1663. break;
  1664. pwWordMaxSel++;
  1665. if (ISDELIMETERW(*pwWordMaxSel))
  1666. spaceLocated = TRUE;
  1667. if (*(pwWordMaxSel - 1) == 0x0A)
  1668. break;
  1669. }
  1670. /*
  1671. * label for fast return ( for Unicode )
  1672. */
  1673. FastReturnW:
  1674. ECUnlock(ped);
  1675. if (pichMin) *pichMin = (ICH)(pwWordMinSel - pwText);
  1676. if (pichMax) *pichMax = (ICH)(pwWordMaxSel - pwText);
  1677. return;
  1678. }
  1679. }
  1680. /***************************************************************************\
  1681. *
  1682. * ECSaveUndo() -
  1683. *
  1684. * Saves old undo information into given buffer, and clears out info in
  1685. * passed in undo buffer. If we're restoring, pundoFrom and pundoTo are
  1686. * reversed.
  1687. *
  1688. \***************************************************************************/
  1689. void ECSaveUndo(PUNDO pundoFrom, PUNDO pundoTo, BOOL fClear)
  1690. {
  1691. /*
  1692. * Save undo data
  1693. */
  1694. RtlCopyMemory(pundoTo, pundoFrom, sizeof(UNDO));
  1695. /*
  1696. * Clear passed in undo buffer
  1697. */
  1698. if (fClear)
  1699. RtlZeroMemory(pundoFrom, sizeof(UNDO) );
  1700. }
  1701. /***************************************************************************\
  1702. * ECEmptyUndo AorW
  1703. *
  1704. * empties the undo buffer.
  1705. *
  1706. * History:
  1707. \***************************************************************************/
  1708. void ECEmptyUndo(
  1709. PUNDO pundo )
  1710. {
  1711. if (pundo->hDeletedText)
  1712. UserGlobalFree(pundo->hDeletedText);
  1713. RtlZeroMemory(pundo, sizeof(UNDO) );
  1714. }
  1715. /***************************************************************************\
  1716. *
  1717. * ECMergeUndoInsertInfo() -
  1718. *
  1719. * When an insert takes place, this function is called with the info about
  1720. * the new insertion (the insertion point and the count of chars inserted);
  1721. * This looks at the existing Undo info and merges the new new insert info
  1722. * with it.
  1723. *
  1724. \***************************************************************************/
  1725. void ECMergeUndoInsertInfo(PUNDO pundo, ICH ichInsert, ICH cchInsert) \
  1726. {
  1727. //
  1728. // If undo buffer is empty, just insert the new info as UNDO_INSERT
  1729. //
  1730. if (pundo->undoType == UNDO_NONE) {
  1731. pundo->undoType = UNDO_INSERT;
  1732. pundo->ichInsStart = ichInsert;
  1733. pundo->ichInsEnd = ichInsert+cchInsert;
  1734. } else if (pundo->undoType & UNDO_INSERT) {
  1735. //
  1736. // If there's already some undo insert info,
  1737. // try to merge the two.
  1738. //
  1739. if (pundo->ichInsEnd == ichInsert) // Check they are adjacent.
  1740. pundo->ichInsEnd += cchInsert; // if so, just concatenate.
  1741. else {
  1742. // The new insert is not contiguous with the old one.
  1743. UNDOINSERT:
  1744. //
  1745. // If there is some UNDO_DELETE info already here, check to see
  1746. // if the new insert takes place at a point different from where
  1747. // that deletion occurred.
  1748. //
  1749. if ((pundo->undoType & UNDO_DELETE) && (pundo->ichDeleted != ichInsert)) {
  1750. //
  1751. // User is inserting into a different point; So, let us
  1752. // forget any UNDO_DELETE info;
  1753. //
  1754. if (pundo->hDeletedText)
  1755. UserGlobalFree(pundo->hDeletedText);
  1756. pundo->hDeletedText = NULL;
  1757. pundo->ichDeleted = 0xFFFFFFFF;
  1758. pundo->undoType &= ~UNDO_DELETE;
  1759. }
  1760. // Since the old insert and new insert are not adjacent, let us
  1761. // forget everything about the old insert and keep just the new
  1762. // insert info as the UNDO_INSERT.
  1763. pundo->ichInsStart = ichInsert;
  1764. pundo->ichInsEnd = ichInsert + cchInsert;
  1765. pundo->undoType |= UNDO_INSERT;
  1766. }
  1767. } else if (pundo->undoType == UNDO_DELETE) {
  1768. // If there is some Delete Info already present go and handle it.
  1769. goto UNDOINSERT;
  1770. }
  1771. }
  1772. /***************************************************************************\
  1773. * ECInsertText AorW
  1774. *
  1775. * Adds cch characters from lpText into the ped->hText starting at
  1776. * ped->ichCaret. Returns TRUE if successful else FALSE. Updates
  1777. * ped->cchAlloc and ped->cch properly if additional memory was allocated or
  1778. * if characters were actually added. Updates ped->ichCaret to be at the end
  1779. * of the inserted text. min and maxsel are equal to ichcaret.
  1780. *
  1781. * History:
  1782. \***************************************************************************/
  1783. BOOL ECInsertText(
  1784. PED ped,
  1785. LPSTR lpText,
  1786. ICH* pcchInsert)
  1787. {
  1788. PSTR pedText;
  1789. PSTR pTextBuff;
  1790. LONG style;
  1791. HANDLE hTextCopy;
  1792. DWORD allocamt;
  1793. //
  1794. // If the last byte (lpText[cchInsert - 1]) is a DBCS leading byte
  1795. // we need to adjust it.
  1796. //
  1797. *pcchInsert = ECAdjustIch(ped, lpText, *pcchInsert);
  1798. if (!*pcchInsert)
  1799. return TRUE;
  1800. /*
  1801. * Do we already have enough memory??
  1802. */
  1803. if (*pcchInsert >= (ped->cchAlloc - ped->cch)) {
  1804. /*
  1805. * Allocate what we need plus a little extra. Return FALSE if we are
  1806. * unsuccessful.
  1807. */
  1808. allocamt = (ped->cch + *pcchInsert) * ped->cbChar;
  1809. allocamt += CCHALLOCEXTRA;
  1810. // if (!ped->fSingle) {
  1811. hTextCopy = LOCALREALLOC(ped->hText, allocamt, LHND, ped->hInstance, &lpText);
  1812. if (hTextCopy) {
  1813. ped->hText = hTextCopy;
  1814. } else {
  1815. return FALSE;
  1816. }
  1817. // } else {
  1818. // if (!LocalReallocSafe(ped->hText, allocamt, LHND, pped))
  1819. // return FALSE;
  1820. // }
  1821. ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
  1822. }
  1823. /*
  1824. * Ok, we got the memory. Now copy the text into the structure
  1825. */
  1826. pedText = ECLock(ped);
  1827. if (ped->pLpkEditCallout) {
  1828. HDC hdc;
  1829. INT iResult;
  1830. hdc = ECGetEditDC (ped, TRUE);
  1831. iResult = ped->pLpkEditCallout->EditVerifyText (ped, hdc, pedText, ped->ichCaret, lpText, *pcchInsert);
  1832. ECReleaseEditDC (ped, hdc, TRUE);
  1833. if (iResult == 0) {
  1834. ECUnlock (ped);
  1835. return TRUE;
  1836. }
  1837. }
  1838. /*
  1839. * Get a pointer to the place where text is to be inserted
  1840. */
  1841. pTextBuff = pedText + ped->ichCaret * ped->cbChar;
  1842. if (ped->ichCaret != ped->cch) {
  1843. /*
  1844. * We are inserting text into the middle. We have to shift text to the
  1845. * right before inserting new text.
  1846. */
  1847. memmove(pTextBuff + *pcchInsert * ped->cbChar, pTextBuff, (ped->cch-ped->ichCaret) * ped->cbChar);
  1848. }
  1849. /*
  1850. * Make a copy of the text being inserted in the edit buffer.
  1851. * Use this copy for doing UPPERCASE/LOWERCASE ANSI/OEM conversions
  1852. * Fix for Bug #3406 -- 01/29/91 -- SANKAR --
  1853. */
  1854. memmove(pTextBuff, lpText, *pcchInsert * ped->cbChar);
  1855. ped->cch += *pcchInsert;
  1856. /*
  1857. * Get the control's style
  1858. */
  1859. style = ped->pwnd->style;
  1860. /*
  1861. * Do the Upper/Lower conversion
  1862. */
  1863. if (style & ES_LOWERCASE) {
  1864. if (ped->fAnsi)
  1865. CharLowerBuffA((LPSTR)pTextBuff, *pcchInsert);
  1866. else
  1867. CharLowerBuffW((LPWSTR)pTextBuff, *pcchInsert);
  1868. } else {
  1869. if (style & ES_UPPERCASE) {
  1870. if (ped->fAnsi) {
  1871. CharUpperBuffA(pTextBuff, *pcchInsert);
  1872. } else {
  1873. CharUpperBuffW((LPWSTR)pTextBuff, *pcchInsert);
  1874. }
  1875. }
  1876. }
  1877. /*
  1878. * Do the OEM conversion
  1879. */
  1880. if ((style & ES_OEMCONVERT) &&
  1881. // For backward compatibility with NT4, we don't perform OEM conversion
  1882. // for older apps if the system locale is FarEast.
  1883. //
  1884. (!IS_DBCS_ENABLED() || GETAPPVER() >= VER50 || GetOEMCP() != GetACP())) {
  1885. ICH i;
  1886. if (ped->fAnsi) {
  1887. for (i = 0; i < *pcchInsert; i++) {
  1888. //
  1889. // We don't need to call CharToOemBuff etc. if the character
  1890. // is a double byte character. And, calling ECIsDBCSLeadByte is
  1891. // faster and less complicated because we don't have to deal
  1892. // with the 2 byte dbcs cases.
  1893. //
  1894. if (IS_DBCS_ENABLED() && ECIsDBCSLeadByte(ped, *(lpText+i))) {
  1895. i++;
  1896. continue;
  1897. }
  1898. //
  1899. // Windows Bug (Whistler) 35289
  1900. // greek has funny rules for casing, so we need to check for it.
  1901. // for nashville we should be doing something more appropriate
  1902. // but for now, leave as Win95 golden
  1903. //
  1904. if (ped->charSet != GREEK_CHARSET && IsCharLowerA(*(pTextBuff + i))) {
  1905. CharUpperBuffA(pTextBuff + i, 1);
  1906. CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
  1907. OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
  1908. CharLowerBuffA(pTextBuff + i, 1);
  1909. } else {
  1910. CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
  1911. OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
  1912. }
  1913. }
  1914. } else {
  1915. //
  1916. // Because 'ch' may become DBCS, and have a space for NULL.
  1917. //
  1918. UCHAR ch[4];
  1919. LPWSTR lpTextW = (LPWSTR)pTextBuff;
  1920. for (i = 0; i < *pcchInsert; i++) {
  1921. if (*(lpTextW + i) == UNICODE_CARRIAGERETURN ||
  1922. *(lpTextW + i) == UNICODE_LINEFEED ||
  1923. *(lpTextW + i) == UNICODE_TAB) {
  1924. continue;
  1925. }
  1926. //
  1927. // Windows Bug (Whistler) 35289
  1928. // greek has funny rules for casing, so we need to check for it.
  1929. // for nashville we should be doing something more appropriate
  1930. // but for now, leave as Win95 golden
  1931. //
  1932. if (ped->charSet != GREEK_CHARSET && IsCharLowerW(*(lpTextW + i))) {
  1933. CharUpperBuffW(lpTextW + i, 1);
  1934. *(LPDWORD)ch = 0; // make sure the null-terminate.
  1935. CharToOemBuffW(lpTextW + i, ch, 1);
  1936. //
  1937. // We assume any SBCS/DBCS character will converted
  1938. // to 1 Unicode char, Otherwise, we may overwrite
  1939. // next character...
  1940. //
  1941. OemToCharBuffW(ch, lpTextW + i, strlen(ch));
  1942. CharLowerBuffW(lpTextW + i, 1);
  1943. } else {
  1944. *(LPDWORD)ch = 0; // make sure the null-terminate.
  1945. CharToOemBuffW(lpTextW + i, ch, 1);
  1946. //
  1947. // We assume any SBCS/DBCS character will converted
  1948. // to 1 Unicode char, Otherwise, we may overwrite
  1949. // next character...
  1950. //
  1951. OemToCharBuffW(ch, lpTextW + i, strlen(ch));
  1952. }
  1953. }
  1954. }
  1955. }
  1956. /* Adjust UNDO fields so that we can undo this insert... */
  1957. ECMergeUndoInsertInfo(Pundo(ped), ped->ichCaret, *pcchInsert);
  1958. ped->ichCaret += *pcchInsert;
  1959. if (ped->pLpkEditCallout) {
  1960. HDC hdc;
  1961. hdc = ECGetEditDC (ped, TRUE);
  1962. ped->ichCaret = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichCaret);
  1963. ECReleaseEditDC (ped, hdc, TRUE);
  1964. }
  1965. ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;
  1966. ECUnlock(ped);
  1967. /*
  1968. * Set dirty bit
  1969. */
  1970. ped->fDirty = TRUE;
  1971. return TRUE;
  1972. }
  1973. /***************************************************************************\
  1974. * ECDeleteText AorW
  1975. *
  1976. * Deletes the text between ped->ichMinSel and ped->ichMaxSel. The
  1977. * character at ichMaxSel is not deleted. But the character at ichMinSel is
  1978. * deleted. ped->cch is updated properly and memory is deallocated if enough
  1979. * text is removed. ped->ichMinSel, ped->ichMaxSel, and ped->ichCaret are set
  1980. * to point to the original ped->ichMinSel. Returns the number of characters
  1981. * deleted.
  1982. *
  1983. * History:
  1984. \***************************************************************************/
  1985. ICH ECDeleteText(
  1986. PED ped)
  1987. {
  1988. PSTR pedText;
  1989. ICH cchDelete;
  1990. LPSTR lpDeleteSaveBuffer;
  1991. HANDLE hDeletedText;
  1992. DWORD bufferOffset;
  1993. cchDelete = ped->ichMaxSel - ped->ichMinSel;
  1994. if (!cchDelete)
  1995. return (0);
  1996. /*
  1997. * Ok, now lets delete the text.
  1998. */
  1999. pedText = ECLock(ped);
  2000. /*
  2001. * Adjust UNDO fields so that we can undo this delete...
  2002. */
  2003. if (ped->undoType == UNDO_NONE) {
  2004. UNDODELETEFROMSCRATCH:
  2005. if (ped->hDeletedText = UserGlobalAlloc(GPTR, (LONG)((cchDelete+1)*ped->cbChar))) {
  2006. ped->undoType = UNDO_DELETE;
  2007. ped->ichDeleted = ped->ichMinSel;
  2008. ped->cchDeleted = cchDelete;
  2009. lpDeleteSaveBuffer = ped->hDeletedText;
  2010. RtlCopyMemory(lpDeleteSaveBuffer, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar);
  2011. lpDeleteSaveBuffer[cchDelete*ped->cbChar] = 0;
  2012. }
  2013. } else if (ped->undoType & UNDO_INSERT) {
  2014. UNDODELETE:
  2015. ECEmptyUndo(Pundo(ped));
  2016. ped->ichInsStart = ped->ichInsEnd = 0xFFFFFFFF;
  2017. ped->ichDeleted = 0xFFFFFFFF;
  2018. ped->cchDeleted = 0;
  2019. goto UNDODELETEFROMSCRATCH;
  2020. } else if (ped->undoType == UNDO_DELETE) {
  2021. if (ped->ichDeleted == ped->ichMaxSel) {
  2022. /*
  2023. * Copy deleted text to front of undo buffer
  2024. */
  2025. hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
  2026. if (!hDeletedText)
  2027. goto UNDODELETE;
  2028. bufferOffset = 0;
  2029. ped->ichDeleted = ped->ichMinSel;
  2030. } else if (ped->ichDeleted == ped->ichMinSel) {
  2031. /*
  2032. * Copy deleted text to end of undo buffer
  2033. */
  2034. hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
  2035. if (!hDeletedText)
  2036. goto UNDODELETE;
  2037. bufferOffset = ped->cchDeleted*ped->cbChar;
  2038. } else {
  2039. /*
  2040. * Clear the current UNDO delete and add the new one since
  2041. the deletes aren't contiguous.
  2042. */
  2043. goto UNDODELETE;
  2044. }
  2045. ped->hDeletedText = hDeletedText;
  2046. lpDeleteSaveBuffer = (LPSTR)hDeletedText;
  2047. if (!bufferOffset) {
  2048. /*
  2049. * Move text in delete buffer up so that we can insert the next
  2050. * text at the head of the buffer.
  2051. */
  2052. RtlMoveMemory(lpDeleteSaveBuffer + cchDelete*ped->cbChar, lpDeleteSaveBuffer,
  2053. ped->cchDeleted*ped->cbChar);
  2054. }
  2055. RtlCopyMemory(lpDeleteSaveBuffer + bufferOffset, pedText + ped->ichMinSel*ped->cbChar,
  2056. cchDelete*ped->cbChar);
  2057. lpDeleteSaveBuffer[(ped->cchDeleted + cchDelete)*ped->cbChar] = 0;
  2058. ped->cchDeleted += cchDelete;
  2059. }
  2060. if (ped->ichMaxSel != ped->cch) {
  2061. /*
  2062. * We are deleting text from the middle of the buffer so we have to
  2063. shift text to the left.
  2064. */
  2065. RtlMoveMemory(pedText + ped->ichMinSel*ped->cbChar, pedText + ped->ichMaxSel*ped->cbChar,
  2066. (ped->cch - ped->ichMaxSel)*ped->cbChar);
  2067. }
  2068. if (ped->cchAlloc - ped->cch > CCHALLOCEXTRA) {
  2069. /*
  2070. * Free some memory since we deleted a lot
  2071. */
  2072. LOCALREALLOC(ped->hText, (DWORD)(ped->cch + (CCHALLOCEXTRA / 2))*ped->cbChar, LHND, ped->hInstance, NULL);
  2073. ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
  2074. }
  2075. ped->cch -= cchDelete;
  2076. if (ped->pLpkEditCallout) {
  2077. HDC hdc;
  2078. hdc = ECGetEditDC (ped, TRUE);
  2079. ped->ichMinSel = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichMinSel);
  2080. ECReleaseEditDC (ped, hdc, TRUE);
  2081. }
  2082. ped->ichCaret = ped->ichMaxSel = ped->ichMinSel;
  2083. ECUnlock(ped);
  2084. /*
  2085. * Set dirty bit
  2086. */
  2087. ped->fDirty = TRUE;
  2088. return (cchDelete);
  2089. }
  2090. /***************************************************************************\
  2091. * ECNotifyParent AorW
  2092. *
  2093. * Sends the notification code to the parent of the edit control
  2094. *
  2095. * History:
  2096. \***************************************************************************/
  2097. void ECNotifyParent(
  2098. PED ped,
  2099. int notificationCode)
  2100. {
  2101. /*
  2102. * wParam is NotificationCode (hiword) and WindowID (loword)
  2103. * lParam is HWND of control sending the message
  2104. * Windows 95 checks for hwndParent != NULL before sending the message, but
  2105. * this is surely rare, and SendMessage NULL hwnd does nowt anyway (IanJa)
  2106. */
  2107. SendMessage(ped->hwndParent, WM_COMMAND,
  2108. (DWORD)MAKELONG(PTR_TO_ID(ped->pwnd->spmenu), notificationCode),
  2109. (LPARAM)ped->hwnd);
  2110. }
  2111. /***************************************************************************\
  2112. *
  2113. * ECSetEditClip() AorW
  2114. *
  2115. * Sets the clip rect for the hdc to the formatting rectangle intersected
  2116. * with the client area.
  2117. *
  2118. \***************************************************************************/
  2119. void ECSetEditClip(PED ped, HDC hdc, BOOL fLeftMargin)
  2120. {
  2121. RECT rcClient;
  2122. RECT rcClip;
  2123. CopyRect(&rcClip, &ped->rcFmt);
  2124. if (ped->pLpkEditCallout) {
  2125. // Complex script handling chooses whether to write margins later
  2126. rcClip.left -= ped->wLeftMargin;
  2127. rcClip.right += ped->wRightMargin;
  2128. } else {
  2129. if (fLeftMargin) /* Should we consider the left margin? */
  2130. rcClip.left -= ped->wLeftMargin;
  2131. if (ped->fWrap) /* Should we consider the right margin? */
  2132. rcClip.right += ped->wRightMargin;
  2133. }
  2134. /* Set clip rectangle to rectClient intersect rectClip */
  2135. /* We must clip for single line edits also. -- B#1360 */
  2136. _GetClientRect(ped->pwnd, &rcClient);
  2137. if (ped->fFlatBorder)
  2138. InflateRect(&rcClient, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  2139. IntersectRect(&rcClient, &rcClient, &rcClip);
  2140. IntersectClipRect(hdc,rcClient.left, rcClient.top,
  2141. rcClient.right, rcClient.bottom);
  2142. }
  2143. /***************************************************************************\
  2144. * ECGetEditDC AorW
  2145. *
  2146. * Hides the caret, gets the DC for the edit control, and clips to
  2147. * the rcFmt rectangle specified for the edit control and sets the proper
  2148. * font. If fFastDC, just select the proper font but don't bother about clip
  2149. * regions or hiding the caret.
  2150. *
  2151. * History:
  2152. \***************************************************************************/
  2153. HDC ECGetEditDC(
  2154. PED ped,
  2155. BOOL fFastDC )
  2156. {
  2157. HDC hdc;
  2158. if (!fFastDC)
  2159. NtUserHideCaret(ped->hwnd);
  2160. if ( hdc = NtUserGetDC(ped->hwnd) ) {
  2161. ECSetEditClip(ped, hdc, (BOOL)(ped->xOffset == 0));
  2162. /*
  2163. * Select the proper font for this edit control's dc.
  2164. */
  2165. if (ped->hFont)
  2166. SelectObject(hdc, ped->hFont);
  2167. }
  2168. return hdc;
  2169. }
  2170. /***************************************************************************\
  2171. * ECReleaseEditDC AorW
  2172. *
  2173. * Releases the DC (hdc) for the edit control and shows the caret.
  2174. * If fFastDC, just select the proper font but don't bother about showing the
  2175. * caret.
  2176. *
  2177. * History:
  2178. \***************************************************************************/
  2179. void ECReleaseEditDC(
  2180. PED ped,
  2181. HDC hdc,
  2182. BOOL fFastDC)
  2183. {
  2184. /*
  2185. * Restoring font not necessary
  2186. */
  2187. ReleaseDC(ped->hwnd, hdc);
  2188. if (!fFastDC)
  2189. NtUserShowCaret(ped->hwnd);
  2190. }
  2191. /***************************************************************************\
  2192. *
  2193. * ECResetTextInfo() AorW
  2194. *
  2195. * Handles a global change to the text by resetting text offsets, emptying
  2196. * the undo buffer, and rebuilding the lines
  2197. *
  2198. \***************************************************************************/
  2199. void ECResetTextInfo(PED ped)
  2200. {
  2201. //
  2202. // Reset caret, selections, scrolling, and dirty information.
  2203. //
  2204. ped->iCaretLine = ped->ichCaret = 0;
  2205. ped->ichMinSel = ped->ichMaxSel = 0;
  2206. ped->xOffset = ped->ichScreenStart = 0;
  2207. ped->fDirty = FALSE;
  2208. ECEmptyUndo(Pundo(ped));
  2209. if (ped->fSingle) {
  2210. if (!ped->listboxHwnd)
  2211. ECNotifyParent(ped, EN_UPDATE);
  2212. } else {
  2213. #ifdef BOGUS
  2214. // B#14640
  2215. // We don't want to strip soft breaks or anything else from text
  2216. // that was passed in by the caller. - karlst.
  2217. MLStripCrCrLf(ped);
  2218. #endif
  2219. MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  2220. }
  2221. if (_IsWindowVisible(ped->pwnd)) {
  2222. BOOL fErase;
  2223. if (ped->fSingle)
  2224. fErase = FALSE;
  2225. else
  2226. fErase = ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines);
  2227. // Always redraw whether or not the insert was successful. We might
  2228. // have NULL text. Paint() will check the redraw flag for us.
  2229. ECInvalidateClient(ped, fErase);
  2230. // BACKWARD COMPAT HACK: RAID expects the text to have been updated,
  2231. // so we have to do an UpdateWindow here. It moves an edit control
  2232. // around with fRedraw == FALSE, so it'll never get the paint message
  2233. // with the control in the right place.
  2234. if (!ped->fWin31Compat)
  2235. UpdateWindow(ped->hwnd);
  2236. }
  2237. if (ped->fSingle && !ped->listboxHwnd) {
  2238. ECNotifyParent(ped, EN_CHANGE);
  2239. }
  2240. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, HW(ped->pwnd), OBJID_CLIENT, INDEXID_CONTAINER);
  2241. }
  2242. /***************************************************************************\
  2243. * ECSetText AorW
  2244. *
  2245. * Copies the null terminated text in lpstr to the ped. Notifies the
  2246. * parent if there isn't enough memory. Sets the minsel, maxsel, and caret to
  2247. * the beginning of the inserted text. Returns TRUE if successful else FALSE
  2248. * if no memory (and notifies the parent).
  2249. *
  2250. * History:
  2251. \***************************************************************************/
  2252. BOOL ECSetText(
  2253. PED ped,
  2254. LPSTR lpstr)
  2255. {
  2256. ICH cchLength;
  2257. ICH cchSave = ped->cch;
  2258. ICH ichCaretSave = ped->ichCaret;
  2259. HWND hwndSave = ped->hwnd;
  2260. HANDLE hText;
  2261. ped->cch = ped->ichCaret = 0;
  2262. ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
  2263. if (!lpstr) {
  2264. hText = LOCALREALLOC(ped->hText, CCHALLOCEXTRA*ped->cbChar, LHND, ped->hInstance, &lpstr);
  2265. if (hText != NULL) {
  2266. ped->hText = hText;
  2267. } else {
  2268. return FALSE;
  2269. }
  2270. } else {
  2271. cchLength = StringLength(lpstr, ped->fAnsi);
  2272. #ifdef NEVER
  2273. // win3.1 does limit single line edit controls to 32K (minus 3) but NT doesn't
  2274. if (ped->fSingle) {
  2275. /*
  2276. * Limit single line edit controls to 32K
  2277. */
  2278. cchLength = min(cchLength, (ICH)(0x7FFD/ped->cbChar));
  2279. }
  2280. #endif
  2281. /*
  2282. * Add the text
  2283. */
  2284. if (cchLength && !ECInsertText(ped, lpstr, &cchLength)) {
  2285. /*
  2286. * Restore original state and notify parent we ran out of memory.
  2287. */
  2288. ped->cch = cchSave;
  2289. ped->ichCaret = ichCaretSave;
  2290. ECNotifyParent(ped, EN_ERRSPACE);
  2291. return FALSE;
  2292. }
  2293. }
  2294. ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
  2295. if (IsWindow(hwndSave))
  2296. ECResetTextInfo(ped);
  2297. return TRUE;
  2298. }
  2299. /***************************************************************************\
  2300. *
  2301. * ECInvalidateClient()
  2302. *
  2303. * Invalidates client of edit field. For old 3.x guys with borders,
  2304. * we draw it ourself (compatibility). So we don't want to invalidate
  2305. * the border or we'll get flicker.
  2306. *
  2307. \***************************************************************************/
  2308. void ECInvalidateClient(PED ped, BOOL fErase)
  2309. {
  2310. if (ped->fFlatBorder) {
  2311. RECT rcT;
  2312. _GetClientRect(ped->pwnd, &rcT);
  2313. InflateRect(&rcT, -SYSMET(CXBORDER),
  2314. -SYSMET(CYBORDER));
  2315. NtUserInvalidateRect(ped->hwnd, &rcT, fErase);
  2316. } else {
  2317. NtUserInvalidateRect(ped->hwnd, NULL, fErase);
  2318. }
  2319. }
  2320. /***************************************************************************\
  2321. * ECCopy AorW
  2322. *
  2323. * Copies the text between ichMinSel and ichMaxSel to the clipboard.
  2324. * Returns the number of characters copied.
  2325. *
  2326. * History:
  2327. \***************************************************************************/
  2328. ICH ECCopy(
  2329. PED ped)
  2330. {
  2331. HANDLE hData;
  2332. char *pchSel;
  2333. char FAR *lpchClip;
  2334. ICH cbData;
  2335. /*
  2336. * Don't allow copies from password style controls
  2337. */
  2338. if (ped->charPasswordChar) {
  2339. NtUserMessageBeep(0);
  2340. return 0;
  2341. }
  2342. cbData = (ped->ichMaxSel - ped->ichMinSel) * ped->cbChar;
  2343. if (!cbData)
  2344. return 0;
  2345. if (!OpenClipboard(ped->hwnd))
  2346. return 0;
  2347. NtUserEmptyClipboard();
  2348. /*
  2349. * If we just called EmptyClipboard in the context of a 16 bit
  2350. * app then we also have to tell WOW to nix its 16 handle copy of
  2351. * clipboard data. WOW does its own clipboard caching because
  2352. * some 16 bit apps use clipboard data even after the clipboard
  2353. * has been emptied. See the note in the server code.
  2354. *
  2355. * Note: this is the only place where EmptyClipboard is called
  2356. * for a 16 bit app not going through WOW. If we added others
  2357. * we might want to move this into EmptyClipboard and have two
  2358. * versions.
  2359. */
  2360. if (GetClientInfo()->CI_flags & CI_16BIT) {
  2361. pfnWowEmptyClipBoard();
  2362. }
  2363. /*
  2364. * +1 for the terminating NULL
  2365. */
  2366. if (!(hData = UserGlobalAlloc(LHND, (LONG)(cbData + ped->cbChar)))) {
  2367. NtUserCloseClipboard();
  2368. return (0);
  2369. }
  2370. USERGLOBALLOCK(hData, lpchClip);
  2371. UserAssert(lpchClip);
  2372. pchSel = ECLock(ped);
  2373. pchSel = pchSel + (ped->ichMinSel * ped->cbChar);
  2374. RtlCopyMemory(lpchClip, pchSel, cbData);
  2375. if (ped->fAnsi)
  2376. *(lpchClip + cbData) = 0;
  2377. else
  2378. *(LPWSTR)(lpchClip + cbData) = (WCHAR)0;
  2379. ECUnlock(ped);
  2380. USERGLOBALUNLOCK(hData);
  2381. SetClipboardData( ped->fAnsi ? CF_TEXT : CF_UNICODETEXT, hData);
  2382. NtUserCloseClipboard();
  2383. return (cbData);
  2384. }
  2385. /***************************************************************************\
  2386. * EditWndProcA
  2387. *
  2388. * Always receives Ansi messages and translates them if appropriate to unicode
  2389. * depending on the PED type
  2390. *
  2391. *
  2392. \***************************************************************************/
  2393. LRESULT EditWndProcA(
  2394. HWND hwnd,
  2395. UINT message,
  2396. WPARAM wParam,
  2397. LPARAM lParam)
  2398. {
  2399. PWND pwnd;
  2400. if ((pwnd = ValidateHwnd(hwnd)) == NULL)
  2401. return 0;
  2402. /*
  2403. * If the control is not interested in this message,
  2404. * pass it to DefWindowProc.
  2405. */
  2406. if (!FWINDOWMSG(message, FNID_EDIT))
  2407. return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
  2408. return EditWndProcWorker(pwnd, message, wParam, lParam, TRUE);
  2409. }
  2410. LRESULT EditWndProcW(
  2411. HWND hwnd,
  2412. UINT message,
  2413. WPARAM wParam,
  2414. LPARAM lParam)
  2415. {
  2416. PWND pwnd;
  2417. if ((pwnd = ValidateHwnd(hwnd)) == NULL)
  2418. return 0;
  2419. /*
  2420. * If the control is not interested in this message,
  2421. * pass it to DefWindowProc.
  2422. */
  2423. if (!FWINDOWMSG(message, FNID_EDIT)) {
  2424. return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
  2425. }
  2426. return EditWndProcWorker(pwnd, message, wParam, lParam, FALSE);
  2427. }
  2428. LRESULT EditWndProcWorker(
  2429. PWND pwnd,
  2430. UINT message,
  2431. WPARAM wParam,
  2432. LPARAM lParam,
  2433. DWORD fAnsi)
  2434. {
  2435. PED ped;
  2436. HWND hwnd = HWq(pwnd);
  2437. static BOOL fInit = TRUE;
  2438. VALIDATECLASSANDSIZE(pwnd, FNID_EDIT);
  2439. INITCONTROLLOOKASIDE(&EditLookaside, ED, pwnd, 4);
  2440. /*
  2441. * Get the ped for the given window now since we will use it a lot in
  2442. * various handlers. This was stored using SetWindowLong(hwnd,0,hped) when
  2443. * we initially created the edit control.
  2444. */
  2445. ped = ((PEDITWND)pwnd)->ped;
  2446. /*
  2447. * Make sure the ANSI flag is set correctly.
  2448. */
  2449. if (!ped->fInitialized) {
  2450. ped->fInitialized = TRUE;
  2451. ped->fAnsi = TestWF(pwnd, WFANSICREATOR) ? TRUE : FALSE;
  2452. }
  2453. /*
  2454. * We just call the regular EditWndProc if the ped is not created, the
  2455. * incoming message type already matches the PED type or the message
  2456. * does not need any translation.
  2457. */
  2458. if (ped->fAnsi == fAnsi ||
  2459. (message >= WM_USER) ||
  2460. !MessageTable[message].bThunkMessage) {
  2461. return EditWndProc(pwnd, message, wParam, lParam);
  2462. }
  2463. return CsSendMessage(hwnd, message, wParam, lParam,
  2464. fAnsi ? (ULONG_PTR)EditWndProcW : (ULONG_PTR)EditWndProcA,
  2465. FNID_CALLWINDOWPROC, fAnsi);
  2466. }
  2467. /***************************************************************************\
  2468. * EditWndProc
  2469. *
  2470. * Class procedure for all edit controls.
  2471. * Dispatches all messages to the appropriate handlers which are named
  2472. * as follows:
  2473. * SL (single line) prefixes all single line edit control procedures while
  2474. * ML (multi line) prefixes all multi- line edit controls.
  2475. * EC (edit control) prefixes all common handlers.
  2476. *
  2477. * The EditWndProc only handles messages common to both single and multi
  2478. * line edit controls. Messages which are handled differently between
  2479. * single and multi are sent to SLEditWndProc or MLEditWndProc.
  2480. *
  2481. * Top level procedures are EditWndPoc, SLEditWndProc, and MLEditWndProc.
  2482. * SL*Handler or ML*Handler or EC*Handler procs are called to handle
  2483. * the various messages. Support procedures are prefixed with SL ML or
  2484. * EC depending on which code they support. They are never called
  2485. * directly and most assumptions/effects are documented in the effects
  2486. * clause.
  2487. *
  2488. * WARNING: If you add a message here, add it to gawEditWndProc[] in
  2489. * kernel\server.c too, otherwise EditWndProcA/W will send it straight to
  2490. * DefWindowProcWorker
  2491. *
  2492. * History:
  2493. \***************************************************************************/
  2494. LRESULT EditWndProc(
  2495. PWND pwnd,
  2496. UINT message,
  2497. WPARAM wParam,
  2498. LPARAM lParam)
  2499. {
  2500. HWND hwnd = HWq(pwnd);
  2501. LRESULT lreturn;
  2502. PED ped;
  2503. /*
  2504. * Get the ped for the given window now since we will use it a lot in
  2505. * various handlers. This was stored using SetWindowLong(hwnd,0,hped) when
  2506. * we initially created the edit control.
  2507. */
  2508. ped = ((PEDITWND)pwnd)->ped;
  2509. /*
  2510. * Dispatch the various messages we can receive
  2511. */
  2512. lreturn = 1L;
  2513. switch (message) {
  2514. /*
  2515. * Messages which are handled the same way for both single and multi line
  2516. * edit controls.
  2517. */
  2518. case WM_KEYDOWN:
  2519. // LPK handling of Ctrl/LShift, Ctrl/RShift
  2520. if (ped && ped->pLpkEditCallout && ped->fAllowRTL) {
  2521. ped->fSwapRoOnUp = FALSE; // Any keydown cancels a ctrl/shift reading order change
  2522. switch (wParam) {
  2523. case VK_SHIFT:
  2524. if ((GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) {
  2525. // Left shift or right shift pressed while control held down
  2526. // Check that alt (VK_MENU) isn't down to avoid false firing on AltGr which equals Ctrl+Alt.
  2527. if (MapVirtualKey((LONG)lParam>>16&0xff, 3) == VK_LSHIFT) {
  2528. // User wants left to right reading order
  2529. ped->fSwapRoOnUp = (ped->fRtoLReading) || (ped->format & ES_RIGHT) ;
  2530. ped->fLShift = TRUE;
  2531. } else {
  2532. // User wants right to left reading order
  2533. ped->fSwapRoOnUp = (!ped->fRtoLReading) || (ped->format & ES_RIGHT);
  2534. ped->fLShift = FALSE;
  2535. }
  2536. }
  2537. break;
  2538. case VK_LEFT:
  2539. if (ped->fRtoLReading) {
  2540. wParam = VK_RIGHT;
  2541. }
  2542. break;
  2543. case VK_RIGHT:
  2544. if (ped->fRtoLReading) {
  2545. wParam = VK_LEFT;
  2546. }
  2547. break;
  2548. }
  2549. }
  2550. goto HandleEditMsg;
  2551. case WM_KEYUP:
  2552. if (ped && ped->pLpkEditCallout && ped->fAllowRTL && ped->fSwapRoOnUp) {
  2553. BOOL fReadingOrder;
  2554. // Complete reading order change detected earlier during keydown
  2555. ped->fSwapRoOnUp = FALSE;
  2556. fReadingOrder = ped->fRtoLReading;
  2557. // Remove any overriding ES_CENTRE or ES_RIGHT format from dwStyle
  2558. SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~ES_FMTMASK);
  2559. if (ped->fLShift) {
  2560. // Set Left to Right reading order and right scrollbar in EX_STYLE
  2561. SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE)
  2562. & ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
  2563. // Edit control is LTR now, then notify the parent.
  2564. ECNotifyParent(ped, EN_ALIGN_LTR_EC);
  2565. // ? Select a keyboard layout appropriate to LTR operation
  2566. } else {
  2567. // Set Right to Left reading order, right alignment and left scrollbar
  2568. SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE)
  2569. | WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
  2570. // Edit control is RTL now, then notify the parent.
  2571. ECNotifyParent(ped, EN_ALIGN_RTL_EC);
  2572. // ? Select a keyboard layout appropriate to RTL operation
  2573. }
  2574. // If reading order didn't change, so we are sure the alignment changed and the edit window didn't invalidate yet.
  2575. if (fReadingOrder == (BOOL) ped->fRtoLReading) {
  2576. ECInvalidateClient(ped, TRUE);
  2577. }
  2578. }
  2579. goto HandleEditMsg;
  2580. case WM_INPUTLANGCHANGE:
  2581. if (ped) {
  2582. // EC_INSERT_COMPOSITION_CHAR : WM_INPUTLANGCHANGE - call ECInitInsert()
  2583. HKL hkl = THREAD_HKL();
  2584. ECInitInsert(ped, hkl);
  2585. if (ped->fInReconversion) {
  2586. ECInOutReconversionMode(ped, FALSE);
  2587. }
  2588. //
  2589. // Font and caret position might be changed while
  2590. // another keyboard layout is active. Set those
  2591. // if the edit control has the focus.
  2592. //
  2593. if (ped->fFocus && fpImmIsIME(hkl)) {
  2594. POINT pt;
  2595. ECImmSetCompositionFont(ped);
  2596. NtUserGetCaretPos(&pt);
  2597. ECImmSetCompositionWindow(ped, pt.x, pt.y);
  2598. }
  2599. }
  2600. goto HandleEditMsg;
  2601. case WM_COPY:
  2602. /*
  2603. * wParam - not used
  2604. * lParam - not used
  2605. */
  2606. lreturn = (LONG)ECCopy(ped);
  2607. break;
  2608. case WM_CUT:
  2609. /*
  2610. *
  2611. * wParamLo -- unused
  2612. * lParam -- unused
  2613. */
  2614. ECCutText(ped);
  2615. return 0;
  2616. case WM_CLEAR:
  2617. /*
  2618. * wParamLo -- unused
  2619. * lParam -- unused
  2620. */
  2621. ECClearText(ped);
  2622. return 0;
  2623. case WM_ENABLE:
  2624. /*
  2625. * wParam - nonzero if window is enabled else disable window if 0.
  2626. * lParam - not used
  2627. */
  2628. lreturn = (LONG)(ped->fDisabled = !((BOOL)wParam));
  2629. ECInvalidateClient(ped, TRUE);
  2630. break;
  2631. case WM_SYSCHAR:
  2632. //
  2633. // wParamLo -- key value
  2634. // lParam -- unused
  2635. //
  2636. //
  2637. // If this is a WM_SYSCHAR message generated by the UNDO
  2638. // keystroke we want to EAT IT
  2639. //
  2640. if ((lParam & SYS_ALTERNATE) && ((WORD)wParam == VK_BACK))
  2641. return TRUE;
  2642. else {
  2643. return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
  2644. }
  2645. break;
  2646. case EM_GETLINECOUNT:
  2647. /*
  2648. * wParam - not used
  2649. lParam - not used
  2650. */
  2651. lreturn = (LONG)ped->cLines;
  2652. break;
  2653. case EM_GETMODIFY:
  2654. /*
  2655. * wParam - not used
  2656. lParam - not used
  2657. */
  2658. /*
  2659. * Gets the state of the modify flag for this edit control.
  2660. */
  2661. lreturn = (LONG)ped->fDirty;
  2662. break;
  2663. case EM_SETMODIFY:
  2664. /*
  2665. * wParam - specifies the new value for the modify flag
  2666. lParam - not used
  2667. */
  2668. /*
  2669. * Sets the state of the modify flag for this edit control.
  2670. */
  2671. ped->fDirty = (wParam != 0);
  2672. break;
  2673. case EM_GETRECT:
  2674. /*
  2675. * wParam - not used
  2676. lParam - pointer to a RECT data structure that gets the dimensions.
  2677. */
  2678. /*
  2679. * Copies the rcFmt rect to *lpRect.
  2680. */
  2681. CopyRect((LPRECT)lParam, (LPRECT)&ped->rcFmt);
  2682. lreturn = (LONG)TRUE;
  2683. break;
  2684. case WM_GETFONT:
  2685. /*
  2686. * wParam - not used
  2687. lParam - not used
  2688. */
  2689. lreturn = (LRESULT)ped->hFont;
  2690. break;
  2691. case WM_SETFONT:
  2692. /*
  2693. * wParam - handle to the font
  2694. lParam - redraw if true else don't
  2695. */
  2696. ECSetFont(ped, (HANDLE)wParam, (BOOL)LOWORD(lParam));
  2697. break;
  2698. case WM_GETTEXT:
  2699. /*
  2700. * wParam - max number of _bytes_ (not characters) to copy
  2701. * lParam - buffer to copy text to. Text is 0 terminated.
  2702. */
  2703. lreturn = (LRESULT)ECGetText(ped, (ICH)wParam, (LPSTR)lParam, TRUE);
  2704. break;
  2705. case WM_SETTEXT:
  2706. //
  2707. // wParamLo -- unused
  2708. // lParam -- LPSTR, null-terminated, with new text.
  2709. //
  2710. lreturn = (LRESULT)ECSetText(ped, (LPSTR)lParam);
  2711. break;
  2712. case WM_GETTEXTLENGTH:
  2713. /*
  2714. * Return count of CHARs!!!
  2715. */
  2716. lreturn = (LONG)ped->cch;
  2717. break;
  2718. case WM_NCDESTROY:
  2719. case WM_FINALDESTROY:
  2720. /*
  2721. * wParam - not used
  2722. lParam - not used
  2723. */
  2724. ECNcDestroyHandler(pwnd, ped);
  2725. return 0;
  2726. /*
  2727. * Most apps (i.e. everyone but Quicken) don't pass on the rbutton
  2728. * messages when they do something with 'em inside of subclassed
  2729. * edit fields. As such, we keep track of whether we saw the
  2730. * down before the up. If we don't see the up, then DefWindowProc
  2731. * won't generate the context menu message, so no big deal. If
  2732. * we didn't see the down, then don't let WM_CONTEXTMENU do
  2733. * anything.
  2734. *
  2735. * We also might want to not generate WM_CONTEXTMENUs for old
  2736. * apps when the mouse is captured.
  2737. */
  2738. case WM_RBUTTONDOWN:
  2739. ped->fSawRButtonDown = TRUE;
  2740. goto HandleEditMsg;
  2741. case WM_RBUTTONUP:
  2742. if (ped->fSawRButtonDown) {
  2743. ped->fSawRButtonDown = FALSE;
  2744. if (!ped->fInReconversion) {
  2745. goto HandleEditMsg;
  2746. }
  2747. }
  2748. // Don't pass this on to DWP so WM_CONTEXTMENU isn't generated.
  2749. return 0;
  2750. case WM_CONTEXTMENU: {
  2751. POINT pt ;
  2752. int nHit = FindNCHit(pwnd, (LONG)lParam);
  2753. if ((nHit == HTVSCROLL) || (nHit == HTHSCROLL)) {
  2754. return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
  2755. }
  2756. POINTSTOPOINT(pt, lParam);
  2757. if (!TestWF(pwnd, WFOLDUI) && ECIsAncestorActive(hwnd))
  2758. ECMenu(hwnd, ped, &pt);
  2759. }
  2760. return 0;
  2761. case EM_CANUNDO:
  2762. /*
  2763. * wParam - not used
  2764. lParam - not used
  2765. */
  2766. lreturn = (LONG)(ped->undoType != UNDO_NONE);
  2767. break;
  2768. case EM_EMPTYUNDOBUFFER:
  2769. /*
  2770. * wParam - not used
  2771. lParam - not used
  2772. */
  2773. ECEmptyUndo(Pundo(ped));
  2774. break;
  2775. case EM_GETMARGINS:
  2776. //
  2777. // wParam -- unused
  2778. // lParam -- unused
  2779. //
  2780. return(MAKELONG(ped->wLeftMargin, ped->wRightMargin));
  2781. case EM_SETMARGINS:
  2782. //
  2783. // wParam -- EC_ margin flags
  2784. // lParam -- LOWORD is left, HIWORD is right margin
  2785. //
  2786. ECSetMargin(ped, (UINT)wParam, (DWORD)lParam, TRUE);
  2787. return 0;
  2788. case EM_GETSEL:
  2789. /*
  2790. * Gets the selection range for the given edit control. The
  2791. * starting position is in the low order word. It contains the position
  2792. * of the first nonselected character after the end of the selection in
  2793. * the high order word.
  2794. */
  2795. if ((PDWORD)wParam != NULL) {
  2796. *((PDWORD)wParam) = ped->ichMinSel;
  2797. }
  2798. if ((PDWORD)lParam != NULL) {
  2799. *((PDWORD)lParam) = ped->ichMaxSel;
  2800. }
  2801. lreturn = MAKELONG(ped->ichMinSel,ped->ichMaxSel);
  2802. break;
  2803. case EM_GETLIMITTEXT:
  2804. //
  2805. // wParamLo -- unused
  2806. // lParam -- unused
  2807. //
  2808. return(ped->cchTextMax);
  2809. case EM_SETLIMITTEXT: /* Renamed from EM_LIMITTEXT in Chicago */
  2810. /*
  2811. * wParam - max number of CHARACTERS that can be entered
  2812. * lParam - not used
  2813. */
  2814. /*
  2815. * Specifies the maximum number of characters of text the user may
  2816. * enter. If maxLength is 0, we may enter MAXINT number of CHARACTERS.
  2817. */
  2818. if (ped->fSingle) {
  2819. if (wParam) {
  2820. wParam = min(0x7FFFFFFEu, wParam);
  2821. } else {
  2822. wParam = 0x7FFFFFFEu;
  2823. }
  2824. }
  2825. if (wParam) {
  2826. ped->cchTextMax = (ICH)wParam;
  2827. } else {
  2828. ped->cchTextMax = 0xFFFFFFFFu;
  2829. }
  2830. break;
  2831. case EM_POSFROMCHAR:
  2832. //
  2833. // Validate that char index is within text range
  2834. //
  2835. if (wParam >= ped->cch) {
  2836. return(-1L);
  2837. }
  2838. goto HandleEditMsg;
  2839. case EM_CHARFROMPOS: {
  2840. // Validate that point is within client of edit field
  2841. RECT rc;
  2842. POINT pt;
  2843. POINTSTOPOINT(pt, lParam);
  2844. _GetClientRect(pwnd, &rc);
  2845. if (!PtInRect(&rc, pt)) {
  2846. return(-1L);
  2847. }
  2848. goto HandleEditMsg;
  2849. }
  2850. case EM_SETPASSWORDCHAR:
  2851. /*
  2852. * wParam - sepecifies the new char to display instead of the
  2853. * real text. if null, display the real text.
  2854. */
  2855. ECSetPasswordChar(ped, (UINT)wParam);
  2856. break;
  2857. case EM_GETPASSWORDCHAR:
  2858. lreturn = (DWORD)ped->charPasswordChar;
  2859. break;
  2860. case EM_SETREADONLY:
  2861. /*
  2862. * wParam - state to set read only flag to
  2863. */
  2864. ped->fReadOnly = (wParam != 0);
  2865. if (wParam)
  2866. SetWindowState(pwnd, EFREADONLY);
  2867. else
  2868. ClearWindowState(pwnd, EFREADONLY);
  2869. lreturn = 1L;
  2870. ECEnableDisableIME( ped );
  2871. // We need to redraw the edit field so that the background color
  2872. // changes. Read-only edits are drawn in CTLCOLOR_STATIC while
  2873. // others are drawn with CTLCOLOR_EDIT.
  2874. ECInvalidateClient(ped, TRUE);
  2875. break;
  2876. case EM_SETWORDBREAKPROC:
  2877. /*
  2878. * wParam - unused
  2879. * lParam - FARPROC address of an app supplied call back function
  2880. */
  2881. ped->lpfnNextWord = (EDITWORDBREAKPROCA)lParam;
  2882. break;
  2883. case EM_GETWORDBREAKPROC:
  2884. lreturn = (LRESULT)ped->lpfnNextWord;
  2885. break;
  2886. // IME
  2887. case EM_GETIMESTATUS:
  2888. // wParam == sub command
  2889. switch (wParam) {
  2890. case EMSIS_COMPOSITIONSTRING:
  2891. return ped->wImeStatus;
  2892. #if 0 // memphis
  2893. case EMSIS_GETLBBIT:
  2894. return (DWORD)ped->bLBBit;
  2895. #endif
  2896. }
  2897. break;
  2898. case EM_SETIMESTATUS:
  2899. // wParam == sub command
  2900. switch (wParam) {
  2901. case EMSIS_COMPOSITIONSTRING:
  2902. ped->wImeStatus = (WORD)lParam;
  2903. }
  2904. break;
  2905. case WM_NCCREATE:
  2906. lreturn = ECNcCreate(ped, pwnd, (LPCREATESTRUCT)lParam);
  2907. break;
  2908. case WM_LBUTTONDOWN:
  2909. //
  2910. // B#3623
  2911. // Don't set focus to edit field if it is within an inactive,
  2912. // captioned child.
  2913. // We might want to version switch this... I haven't found
  2914. // any problems by not, but you never know...
  2915. //
  2916. if (ECIsAncestorActive(hwnd)) {
  2917. /*
  2918. * Reconversion support: quit reconversion if left button is clicked.
  2919. * Otherwise, if the current KL is Korean, finailize the composition string.
  2920. */
  2921. if (ped->fInReconversion || ped->fKorea) {
  2922. BOOLEAN fReconversion = (BOOLEAN)ped->fInReconversion;
  2923. DWORD dwIndex = fReconversion ? CPS_CANCEL : CPS_COMPLETE;
  2924. HIMC hImc;
  2925. ped->fReplaceCompChr = FALSE;
  2926. hImc = fpImmGetContext(ped->hwnd);
  2927. if (hImc) {
  2928. fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, dwIndex, 0);
  2929. fpImmReleaseContext(ped->hwnd, hImc);
  2930. }
  2931. if (fReconversion) {
  2932. ECInOutReconversionMode(ped, FALSE);
  2933. }
  2934. ECSetCaretHandler(ped);
  2935. }
  2936. goto HandleEditMsg;
  2937. }
  2938. break;
  2939. case WM_MOUSEMOVE:
  2940. //
  2941. // We only care about mouse messages when mouse is down.
  2942. //
  2943. if (ped->fMouseDown)
  2944. goto HandleEditMsg;
  2945. break;
  2946. case WM_IME_SETCONTEXT:
  2947. //
  2948. // If ped->fInsertCompChr is TRUE, that means we will do
  2949. // all the composition character drawing by ourself.
  2950. //
  2951. if ( ped->fInsertCompChr ) {
  2952. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  2953. }
  2954. if ( wParam ) {
  2955. PINPUTCONTEXT pInputContext;
  2956. HIMC hImc;
  2957. hImc = fpImmGetContext( hwnd );
  2958. if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) {
  2959. pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS;
  2960. fpImmUnlockIMC( hImc );
  2961. }
  2962. if (GetClientInfo()->CI_flags & CI_16BIT) {
  2963. fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L);
  2964. }
  2965. fpImmReleaseContext( hwnd, hImc );
  2966. }
  2967. return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
  2968. case WM_IME_ENDCOMPOSITION:
  2969. ECInOutReconversionMode(ped, FALSE);
  2970. if (ped->fReplaceCompChr) {
  2971. ICH ich;
  2972. HDC hdc;
  2973. //
  2974. // we have a DBCS character to be replaced.
  2975. // let's delete it before inserting the new one.
  2976. //
  2977. ich = (ped->fAnsi) ? 2 : 1;
  2978. ped->fReplaceCompChr = FALSE;
  2979. ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch);
  2980. ped->ichMinSel = ped->ichCaret;
  2981. if (ped->fSingle) {
  2982. if (ECDeleteText( ped ) > 0) {
  2983. //
  2984. // Update the display
  2985. //
  2986. ECNotifyParent(ped, EN_UPDATE);
  2987. hdc = ECGetEditDC(ped, FALSE);
  2988. SLDrawText(ped, hdc, 0);
  2989. ECReleaseEditDC(ped, hdc, FALSE);
  2990. //
  2991. // Tell parent our text contents changed.
  2992. //
  2993. ECNotifyParent(ped, EN_CHANGE);
  2994. }
  2995. }
  2996. else {
  2997. MLDeleteText(ped);
  2998. }
  2999. ECSetCaretHandler( ped );
  3000. }
  3001. return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
  3002. case WM_IME_STARTCOMPOSITION:
  3003. if ( ped->fInsertCompChr ) {
  3004. //
  3005. // NOTE:
  3006. // sending WM_IME_xxxCOMPOSITION will let
  3007. // IME draw composition window. IME should
  3008. // not do that since we cleared
  3009. // ISC_SHOWUICOMPOSITIONWINDOW bit when
  3010. // we got WM_IME_SETCONTEXT message.
  3011. //
  3012. // Korean IME should be fixed in the future.
  3013. //
  3014. break;
  3015. } else {
  3016. return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
  3017. }
  3018. // simple composition character support for FE IME.
  3019. case WM_IME_COMPOSITION:
  3020. return ECImeComposition(ped, wParam, lParam);
  3021. case WM_KILLFOCUS:
  3022. //
  3023. // when focus is removed from the window,
  3024. // composition character should be finalized
  3025. //
  3026. if (ped && fpImmIsIME(THREAD_HKL())) {
  3027. HIMC hImc = fpImmGetContext(hwnd);
  3028. if (hImc != NULL_HIMC) {
  3029. if (ped->fReplaceCompChr || (ped->wImeStatus & EIMES_COMPLETECOMPSTRKILLFOCUS)) {
  3030. // If the composition string to be determined upon kill focus,
  3031. // do it now.
  3032. fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  3033. } else if (ped->fInReconversion) {
  3034. // If the composition string it not to be determined,
  3035. // and if we're in reconversion mode, cancel reconversion now.
  3036. fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  3037. }
  3038. // Get out from reconversion mode
  3039. if (ped->fInReconversion) {
  3040. ECInOutReconversionMode(ped, FALSE);
  3041. }
  3042. fpImmReleaseContext(hwnd, hImc);
  3043. }
  3044. }
  3045. goto HandleEditMsg;
  3046. break;
  3047. case WM_SETFOCUS:
  3048. if (ped && !ped->fFocus) {
  3049. HKL hkl = THREAD_HKL();
  3050. if (fpImmIsIME(hkl)) {
  3051. HIMC hImc;
  3052. hImc = fpImmGetContext(hwnd);
  3053. if (hImc) {
  3054. LPINPUTCONTEXT lpImc;
  3055. if (ped->wImeStatus & EIMES_CANCELCOMPSTRINFOCUS) {
  3056. // cancel when in-focus
  3057. fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  3058. }
  3059. ECImmSetCompositionFont(ped);
  3060. if ((lpImc = fpImmLockIMC(hImc)) != NULL) {
  3061. // We presume the CompForm will reset to CFS_DEFAULT,
  3062. // when the edit control loses Focus.
  3063. // IMEWndProc32 will call ImmSetCompositionWindow with
  3064. // CFS_DEFAULT, when it receive WM_IME_SETCONTEXT.
  3065. lpImc->fdw31Compat |= F31COMPAT_ECSETCFS;
  3066. fpImmUnlockIMC(hImc);
  3067. }
  3068. fpImmReleaseContext(hwnd, hImc);
  3069. }
  3070. //
  3071. // force to set IME composition window when
  3072. // first getting focus.
  3073. //
  3074. ped->ptScreenBounding.x = -1;
  3075. ped->ptScreenBounding.y = -1;
  3076. }
  3077. /*
  3078. * Insert and replace flags are initialized when the edit control gets the focus.
  3079. *
  3080. * Compat hack: a bogus application tries to cheat the system by sending input messages
  3081. * *before* it sets the focus to the edit control. They rely on the flags not being set
  3082. * at WM_SETFOCUS. Raid #411686
  3083. */
  3084. if ((GetAppCompatFlags2(VER40) & GACF2_NO_INIT_ECFLAGS_ON_SETFOCUS) == 0) {
  3085. ECInitInsert(ped, hkl);
  3086. }
  3087. }
  3088. goto HandleEditMsg;
  3089. break;
  3090. case WM_IME_REQUEST:
  3091. // simple ImeRequest Handler
  3092. return EcImeRequestHandler(ped, wParam, lParam);
  3093. case WM_CREATE:
  3094. if (ped)
  3095. ECEnableDisableIME(ped);
  3096. goto HandleEditMsg;
  3097. break;
  3098. default:
  3099. HandleEditMsg:
  3100. /* (picked up from NT40FE SP3)
  3101. * HACK ALERT: We may receive messages before the PED has been
  3102. * allocated (eg: WM_GETMINMAXINFO is sent before WM_NCCREATE)
  3103. * so we must test ped before dreferencing.
  3104. */
  3105. if (ped != NULL) {
  3106. if (ped->fSingle) {
  3107. lreturn = SLEditWndProc(hwnd, ped, message, wParam, lParam);
  3108. } else {
  3109. lreturn = MLEditWndProc(hwnd, ped, message, wParam, lParam);
  3110. }
  3111. }
  3112. }
  3113. return lreturn;
  3114. }
  3115. /***************************************************************************\
  3116. * ECFindXORblks
  3117. *
  3118. * This finds the XOR of lpOldBlk and lpNewBlk and return s resulting blocks
  3119. * through the lpBlk1 and lpBlk2; This could result in a single block or
  3120. * at the maximum two blocks;
  3121. * If a resulting block is empty, then it's StPos field has -1.
  3122. * NOTE:
  3123. * When called from MultiLine edit control, StPos and EndPos fields of
  3124. * these blocks have the Starting line and Ending line of the block;
  3125. * When called from SingleLine edit control, StPos and EndPos fields
  3126. * of these blocks have the character index of starting position and
  3127. * ending position of the block.
  3128. *
  3129. * History:
  3130. \***************************************************************************/
  3131. void ECFindXORblks(
  3132. LPBLOCK lpOldBlk,
  3133. LPBLOCK lpNewBlk,
  3134. LPBLOCK lpBlk1,
  3135. LPBLOCK lpBlk2)
  3136. {
  3137. if (lpOldBlk->StPos >= lpNewBlk->StPos) {
  3138. lpBlk1->StPos = lpNewBlk->StPos;
  3139. lpBlk1->EndPos = min(lpOldBlk->StPos, lpNewBlk->EndPos);
  3140. } else {
  3141. lpBlk1->StPos = lpOldBlk->StPos;
  3142. lpBlk1->EndPos = min(lpNewBlk->StPos, lpOldBlk->EndPos);
  3143. }
  3144. if (lpOldBlk->EndPos <= lpNewBlk->EndPos) {
  3145. lpBlk2->StPos = max(lpOldBlk->EndPos, lpNewBlk->StPos);
  3146. lpBlk2->EndPos = lpNewBlk->EndPos;
  3147. } else {
  3148. lpBlk2->StPos = max(lpNewBlk->EndPos, lpOldBlk->StPos);
  3149. lpBlk2->EndPos = lpOldBlk->EndPos;
  3150. }
  3151. }
  3152. /***************************************************************************\
  3153. * ECCalcChangeSelection
  3154. *
  3155. * This function finds the XOR between two selection blocks(OldBlk and NewBlk)
  3156. * and return s the resulting areas thro the same parameters; If the XOR of
  3157. * both the blocks is empty, then this return s FALSE; Otherwise TRUE.
  3158. *
  3159. * NOTE:
  3160. * When called from MultiLine edit control, StPos and EndPos fields of
  3161. * these blocks have the Starting line and Ending line of the block;
  3162. * When called from SingleLine edit control, StPos and EndPos fields
  3163. * of these blocks have the character index of starting position and
  3164. * ending position of the block.
  3165. *
  3166. * History:
  3167. \***************************************************************************/
  3168. BOOL ECCalcChangeSelection(
  3169. PED ped,
  3170. ICH ichOldMinSel,
  3171. ICH ichOldMaxSel,
  3172. LPBLOCK OldBlk,
  3173. LPBLOCK NewBlk)
  3174. {
  3175. BLOCK Blk[2];
  3176. int iBlkCount = 0;
  3177. Blk[0].StPos = Blk[0].EndPos = Blk[1].StPos = Blk[1].EndPos = 0xFFFFFFFF;
  3178. /*
  3179. * Check if the Old selection block existed
  3180. */
  3181. if (ichOldMinSel != ichOldMaxSel) {
  3182. /*
  3183. * Yes! Old block existed.
  3184. */
  3185. Blk[0].StPos = OldBlk->StPos;
  3186. Blk[0].EndPos = OldBlk->EndPos;
  3187. iBlkCount++;
  3188. }
  3189. /*
  3190. * Check if the new Selection block exists
  3191. */
  3192. if (ped->ichMinSel != ped->ichMaxSel) {
  3193. /*
  3194. * Yes! New block exists
  3195. */
  3196. Blk[1].StPos = NewBlk->StPos;
  3197. Blk[1].EndPos = NewBlk->EndPos;
  3198. iBlkCount++;
  3199. }
  3200. /*
  3201. * If both the blocks exist find the XOR of them
  3202. */
  3203. if (iBlkCount == 2) {
  3204. /*
  3205. * Check if both blocks start at the same character position
  3206. */
  3207. if (ichOldMinSel == ped->ichMinSel) {
  3208. /*
  3209. * Check if they end at the same character position
  3210. */
  3211. if (ichOldMaxSel == ped->ichMaxSel)
  3212. return FALSE; /* Nothing changes */
  3213. Blk[0].StPos = min(NewBlk -> EndPos, OldBlk -> EndPos);
  3214. Blk[0].EndPos = max(NewBlk -> EndPos, OldBlk -> EndPos);
  3215. Blk[1].StPos = 0xFFFFFFFF;
  3216. } else {
  3217. if (ichOldMaxSel == ped->ichMaxSel) {
  3218. Blk[0].StPos = min(NewBlk->StPos, OldBlk->StPos);
  3219. Blk[0].EndPos = max(NewBlk->StPos, OldBlk->StPos);
  3220. Blk[1].StPos = 0xFFFFFFFF;
  3221. } else {
  3222. ECFindXORblks(OldBlk, NewBlk, &Blk[0], &Blk[1]);
  3223. }
  3224. }
  3225. }
  3226. RtlCopyMemory(OldBlk, &Blk[0], sizeof(BLOCK));
  3227. RtlCopyMemory(NewBlk, &Blk[1], sizeof(BLOCK));
  3228. return TRUE; /* Yup , There is something to paint */
  3229. }
  3230. /***************************************************************************\
  3231. * ECGetControlBrush
  3232. *
  3233. * Client side optimization replacement for NtUserGetControlBrush
  3234. *
  3235. * message is one of the WM_CTLCOLOR* messages.
  3236. *
  3237. \***************************************************************************/
  3238. HBRUSH ECGetControlBrush(
  3239. PED ped,
  3240. HDC hdc,
  3241. LONG message)
  3242. {
  3243. PWND pwndSend;
  3244. PWND pwndEdit;
  3245. pwndEdit = ValidateHwnd(ped->hwnd);
  3246. if (pwndEdit == (PWND)NULL)
  3247. return (HBRUSH)0;
  3248. if ((pwndSend = (TestwndPopup(pwndEdit) ? pwndEdit->spwndOwner : pwndEdit->spwndParent)) == NULL)
  3249. pwndSend = pwndEdit;
  3250. else
  3251. pwndSend = REBASEPTR(pwndEdit, pwndSend);
  3252. UserAssert(pwndSend);
  3253. if (PtiCurrent() != GETPTI(pwndSend)) {
  3254. return (HBRUSH)DefWindowProcWorker(pwndSend, message,
  3255. (WPARAM)hdc, (LPARAM)pwndEdit, ped->fAnsi);
  3256. }
  3257. /*
  3258. * By using the correct A/W call we avoid a c/s transition
  3259. * on this SendMessage().
  3260. */
  3261. return (HBRUSH)SendMessageWorker(pwndSend, message, (WPARAM)hdc,
  3262. (LPARAM)ped->hwnd, ped->fAnsi);
  3263. }
  3264. UINT WINAPI QueryFontAssocStatus(void);
  3265. UINT fFontAssocStatus = 0xffff;
  3266. /***************************************************************************\
  3267. * ECGetDBCSVector( PED ped, BYTE CharSet )
  3268. *
  3269. * This function sets DBCS Vector for specified character set and sets
  3270. * ped->fDBCS flag if needed.
  3271. *
  3272. * History: 18-Jun-1996 Hideyuki Nagase
  3273. \***************************************************************************/
  3274. int ECGetDBCSVector(PED ped, HDC hdc, BYTE CharSet)
  3275. {
  3276. BOOL bDBCSCodePage = FALSE;
  3277. /*
  3278. * if DEFAUT_CHARSET was passed, we will convert that to Shell charset..
  3279. */
  3280. if (CharSet == DEFAULT_CHARSET) {
  3281. CharSet = (BYTE)GetTextCharset(hdc);
  3282. /*
  3283. * if CharSet is still DEFAULT_CHARSET, it means gdi has some problem..
  3284. * then just return default.. we get charset from CP_ACP..
  3285. */
  3286. if (CharSet == DEFAULT_CHARSET) {
  3287. CharSet = (BYTE)GetACPCharSet();
  3288. }
  3289. }
  3290. switch (CharSet) {
  3291. case SHIFTJIS_CHARSET:
  3292. case HANGEUL_CHARSET:
  3293. case CHINESEBIG5_CHARSET:
  3294. case GB2312_CHARSET:
  3295. bDBCSCodePage = TRUE;
  3296. break;
  3297. case ANSI_CHARSET: // 0
  3298. case SYMBOL_CHARSET: // 2
  3299. case OEM_CHARSET: // 255
  3300. if (fFontAssocStatus == 0xffff)
  3301. fFontAssocStatus = QueryFontAssocStatus();
  3302. if ((((CharSet + 2) & 0xf) & fFontAssocStatus)) {
  3303. bDBCSCodePage = TRUE;
  3304. /*
  3305. * Bug 117558, etc.
  3306. * Try to get a meaningful character set for associated font.
  3307. */
  3308. CharSet = (BYTE)GetACPCharSet();
  3309. } else {
  3310. bDBCSCodePage = FALSE;
  3311. }
  3312. break;
  3313. default:
  3314. bDBCSCodePage = FALSE;
  3315. }
  3316. if (bDBCSCodePage) {
  3317. CHARSETINFO CharsetInfo;
  3318. DWORD CodePage;
  3319. CPINFO CPInfo;
  3320. int lbIX;
  3321. if (TranslateCharsetInfo((DWORD *)CharSet, &CharsetInfo, TCI_SRCCHARSET)) {
  3322. CodePage = CharsetInfo.ciACP;
  3323. } else {
  3324. CodePage = CP_ACP;
  3325. }
  3326. GetCPInfo(CodePage, &CPInfo);
  3327. for (lbIX=0 ; CPInfo.LeadByte[lbIX] != 0 ; lbIX+=2) {
  3328. ped->DBCSVector[lbIX ] = CPInfo.LeadByte[lbIX];
  3329. ped->DBCSVector[lbIX+1] = CPInfo.LeadByte[lbIX+1];
  3330. }
  3331. ped->DBCSVector[lbIX ] = 0x0;
  3332. ped->DBCSVector[lbIX+1] = 0x0;
  3333. } else {
  3334. ped->DBCSVector[0] = 0x0;
  3335. ped->DBCSVector[1] = 0x0;
  3336. }
  3337. //
  3338. // Final check: if the font supports DBCS glyphs
  3339. //
  3340. // If we've got a font with DBCS glyphs, let's mark PED so.
  3341. // But since the font's primary charset is the one other than FE,
  3342. // we can only support UNICODE Edit control.
  3343. //
  3344. // a) GDI performs A/W conversion for ANSI apps based on the primary
  3345. // character set in hDC, so it will break anyway.
  3346. // b) ANSI applications are only supported on their native system locales:
  3347. // GetACPCharSet() is expected to return a FE code page.
  3348. // c) ANSI Edit control requires DBCSVector, which cannot be
  3349. // initialized without a FE code page.
  3350. //
  3351. if (!ped->fAnsi) {
  3352. FONTSIGNATURE fontSig;
  3353. GetTextCharsetInfo(hdc, &fontSig, 0);
  3354. if (fontSig.fsCsb[0] & FAREAST_CHARSET_BITS) {
  3355. bDBCSCodePage = TRUE;
  3356. // Since this is UNICODE, we're not
  3357. }
  3358. }
  3359. return bDBCSCodePage;
  3360. }
  3361. /***************************************************************************\
  3362. * LPSTR ECAnsiNext( ped, lpCurrent )
  3363. *
  3364. * This function advances string pointer for Edit Control use only.
  3365. *
  3366. * History:
  3367. \***************************************************************************/
  3368. LPSTR ECAnsiNext(PED ped, LPSTR lpCurrent)
  3369. {
  3370. return lpCurrent+((ECIsDBCSLeadByte(ped,*lpCurrent)==TRUE) ? 2 : 1);
  3371. }
  3372. /***************************************************************************\
  3373. * LPSTR ECAnsiPrev( ped, lpBase, lpStr )
  3374. *
  3375. * This function decrements string pointer for Edit Control use only.
  3376. *
  3377. * History:
  3378. \***************************************************************************/
  3379. LPSTR ECAnsiPrev(PED ped, LPSTR lpBase, LPSTR lpStr )
  3380. {
  3381. LPSTR lpCurrent = lpStr -1;
  3382. if (!ped->fDBCS)
  3383. return lpCurrent; // just return ( lpStr - 1 )
  3384. if (lpBase >= lpCurrent)
  3385. return lpBase;
  3386. if (ECIsDBCSLeadByte(ped, *lpCurrent)) // this check makes things faster
  3387. return (lpCurrent - 1); // 92/04/04 takaok
  3388. do {
  3389. lpCurrent--;
  3390. if (!ECIsDBCSLeadByte(ped, *lpCurrent)) {
  3391. lpCurrent++;
  3392. break;
  3393. }
  3394. } while(lpCurrent != lpBase);
  3395. return lpStr - (((lpStr - lpCurrent) & 1) ? 1 : 2);
  3396. }
  3397. /***************************************************************************\
  3398. * ICH ECNextIch( ped, pText, ichCurrent )
  3399. *
  3400. * This function advances string pointer for Edit Control use only.
  3401. *
  3402. * History:
  3403. \***************************************************************************/
  3404. ICH ECNextIch( PED ped, LPSTR pStart, ICH ichCurrent )
  3405. {
  3406. if (!ped->fDBCS || !ped->fAnsi) {
  3407. return (ichCurrent + 1);
  3408. } else {
  3409. ICH ichRet;
  3410. LPSTR pText;
  3411. if (pStart)
  3412. pText = pStart + ichCurrent;
  3413. else
  3414. pText = (LPSTR)ECLock(ped) + ichCurrent;
  3415. ichRet = ichCurrent + ( ECIsDBCSLeadByte(ped, *pText) ? 2 : 1 );
  3416. if (!pStart)
  3417. ECUnlock(ped);
  3418. return (ichRet);
  3419. }
  3420. }
  3421. /***************************************************************************\
  3422. * ICH ECPrevIch( ped, LPSTR pStart, ICH ichCurrent )
  3423. *
  3424. * This function decrements string pointer for Edit Control use only.
  3425. *
  3426. * History:
  3427. \***************************************************************************/
  3428. ICH ECPrevIch( PED ped, LPSTR pStart, ICH ichCurrent )
  3429. {
  3430. LPSTR lpCurrent;
  3431. LPSTR lpStr;
  3432. LPSTR lpBase;
  3433. #ifdef SURROGATE
  3434. // Handle Unicode surrogates pairs when CSLPK is loaded
  3435. if (ped->fAnsi || !ped->pLpkEditCallout) // if no surrogate processing required
  3436. #endif
  3437. if (!ped->fDBCS || !ped->fAnsi)
  3438. if ( ichCurrent )
  3439. return (ichCurrent - 1);
  3440. else
  3441. return (ichCurrent);
  3442. if (ichCurrent <= 1)
  3443. return 0;
  3444. if (pStart)
  3445. lpBase = pStart;
  3446. else
  3447. lpBase = ECLock(ped);
  3448. #ifdef SURROGATE
  3449. // Handle characters represented by multiple codepoints
  3450. if (ped->fAnsi) {
  3451. // ANSI PrevIch with DBCS support
  3452. #endif
  3453. lpStr = lpBase + ichCurrent;
  3454. lpCurrent = lpStr - 1;
  3455. if (ECIsDBCSLeadByte(ped,*lpCurrent)) {
  3456. if (!pStart)
  3457. ECUnlock(ped);
  3458. return (ichCurrent - 2);
  3459. }
  3460. do {
  3461. lpCurrent--;
  3462. if (!ECIsDBCSLeadByte(ped, *lpCurrent)) {
  3463. lpCurrent++;
  3464. break;
  3465. }
  3466. } while(lpCurrent != lpBase);
  3467. if (!pStart)
  3468. ECUnlock(ped);
  3469. return (ichCurrent - (((lpStr - lpCurrent) & 1) ? 1 : 2));
  3470. #ifdef SURROGATE
  3471. } else {
  3472. // Unicode PrevIch with surrogate pair support
  3473. ichCurrent--;
  3474. if ( (((WCHAR*)lpBase)[ichCurrent] & 0xFC00) == 0xDC00
  3475. && (((WCHAR*)lpBase)[ichCurrent-1] & 0xFC00) == 0xD800) {
  3476. ichCurrent--;
  3477. }
  3478. if (!pStart)
  3479. ECUnlock(ped);
  3480. return ichCurrent;
  3481. }
  3482. #endif
  3483. }
  3484. /***************************************************************************\
  3485. * BOOL ECIsDBCSLeadByte( PED ped, BYTE cch )
  3486. *
  3487. * IsDBCSLeadByte for Edit Control use only.
  3488. *
  3489. * History: 18-Jun-1996 Hideyuki Nagase
  3490. \***************************************************************************/
  3491. BOOL ECIsDBCSLeadByte(PED ped, BYTE cch)
  3492. {
  3493. int i;
  3494. if (!ped->fDBCS || !ped->fAnsi)
  3495. return (FALSE);
  3496. for (i = 0; ped->DBCSVector[i]; i += 2) {
  3497. if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch))
  3498. return (TRUE);
  3499. }
  3500. return (FALSE);
  3501. }
  3502. /***************************************************************************\
  3503. * int DBCSCombine(HWND hwnd, int ch)
  3504. *
  3505. * Assemble two WM_CHAR messages to single DBCS character.
  3506. * If program detects first byte of DBCS character in WM_CHAR message,
  3507. * it calls this function to obtain second WM_CHAR message from queue.
  3508. * finally this routine assembles first byte and second byte into single
  3509. * DBCS character.
  3510. *
  3511. * History:
  3512. \***************************************************************************/
  3513. WORD DbcsCombine(HWND hwnd, WORD ch)
  3514. {
  3515. MSG msg;
  3516. int i = 10; /* loop counter to avoid the infinite loop */
  3517. while (!PeekMessageA(&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
  3518. if (--i == 0)
  3519. return 0;
  3520. Sleep(1);
  3521. }
  3522. return (WORD)ch | ((WORD)(msg.wParam) << 8);
  3523. }
  3524. /***************************************************************************\
  3525. * ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch )
  3526. *
  3527. * This function adjusts a current pointer correctly. If a current
  3528. * pointer is lying between DBCS first byte and second byte, this
  3529. * function adjusts a current pointer to a first byte of DBCS position
  3530. * by decrement once.
  3531. *
  3532. * History:
  3533. \***************************************************************************/
  3534. ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch )
  3535. {
  3536. ICH newch = ch;
  3537. if (!ped->fAnsi || !ped->fDBCS || newch == 0)
  3538. return ( ch );
  3539. if (!ECIsDBCSLeadByte(ped,lpstr[--newch]))
  3540. return ( ch ); // previous char is SBCS
  3541. while(1) {
  3542. if (!ECIsDBCSLeadByte(ped,lpstr[newch])) {
  3543. newch++;
  3544. break;
  3545. }
  3546. if (newch)
  3547. newch--;
  3548. else
  3549. break;
  3550. }
  3551. return ((ch - newch) & 1) ? ch-1 : ch;
  3552. }
  3553. /***************************************************************************\
  3554. * ICH ECAdjustIchNext( PED ped, LPSTR lpstr, ICH ch )
  3555. *
  3556. * History:
  3557. * 19.Jun.1996 Hideyuki Nagase [hideyukn] - Port from Win95-FarEast version
  3558. \***************************************************************************/
  3559. ICH FAR PASCAL ECAdjustIchNext(PED ped, LPSTR lpstr, ICH ch)
  3560. {
  3561. ICH ichNew = ECAdjustIch(ped,lpstr,ch);
  3562. LPSTR lpnew = lpstr+ichNew;
  3563. // if ch > ichNew then ECAdjustIch adjusted ich.
  3564. if (ch > ichNew)
  3565. lpnew = ECAnsiNext(ped, lpnew);
  3566. return (ICH)(lpnew-lpstr);
  3567. }
  3568. /***************************************************************************\
  3569. * ECUpdateFormat
  3570. *
  3571. * Computes ped->format and ped->fRtoLReading from dwStyle and dwExStyle.
  3572. * Refreshes the display if either are changed.
  3573. *
  3574. * History:
  3575. * May 12, 1997 [samera] wrote it
  3576. * May 12, 1997 [dbrown] rewrote it
  3577. \***************************************************************************/
  3578. void ECUpdateFormat(
  3579. PED ped,
  3580. DWORD dwStyle,
  3581. DWORD dwExStyle)
  3582. {
  3583. UINT fNewRtoLReading;
  3584. UINT uiNewFormat;
  3585. // Extract new format and reading order from style
  3586. fNewRtoLReading = dwExStyle & WS_EX_RTLREADING ? 1 : 0;
  3587. uiNewFormat = dwStyle & ES_FMTMASK;
  3588. // WS_EX_RIGHT is ignored unless dwStyle is ES_LEFT
  3589. if (uiNewFormat == ES_LEFT && dwExStyle & WS_EX_RIGHT) {
  3590. uiNewFormat = ES_RIGHT;
  3591. }
  3592. // Internally ES_LEFT and ES_RIGHT are swapped for RtoLReading order
  3593. // (Think of them as ES_LEADING and ES_TRAILING)
  3594. if (fNewRtoLReading) {
  3595. switch (uiNewFormat) {
  3596. case ES_LEFT: uiNewFormat = ES_RIGHT; break;
  3597. case ES_RIGHT: uiNewFormat = ES_LEFT; break;
  3598. }
  3599. }
  3600. // Format change does not cause redisplay by itself
  3601. ped->format = uiNewFormat;
  3602. // Refresh display on change of reading order
  3603. if (fNewRtoLReading != ped->fRtoLReading) {
  3604. ped->fRtoLReading = fNewRtoLReading;
  3605. if (ped->fWrap) {
  3606. // Redo wordwrap
  3607. MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  3608. MLUpdateiCaretLine(ped);
  3609. } else {
  3610. // Refresh horizontal scrollbar display
  3611. MLScroll(ped, FALSE, 0xffffffff, 0, TRUE);
  3612. }
  3613. ECInvalidateClient(ped, TRUE);
  3614. }
  3615. }