Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5770 lines
155 KiB

  1. #include "ctlspriv.h"
  2. #pragma hdrstop
  3. #include "usrctl32.h"
  4. #include "edit.h"
  5. //---------------------------------------------------------------------------//
  6. //
  7. // Forwards
  8. //
  9. ICH Edit_FindTabA(LPSTR, ICH);
  10. ICH Edit_FindTabW(LPWSTR, ICH);
  11. HBRUSH Edit_GetControlBrush(PED, HDC, LONG);
  12. NTSYSAPI
  13. VOID
  14. NTAPI
  15. RtlRunEncodeUnicodeString(
  16. PUCHAR Seed OPTIONAL,
  17. PUNICODE_STRING String
  18. );
  19. NTSYSAPI
  20. VOID
  21. NTAPI
  22. RtlRunDecodeUnicodeString(
  23. UCHAR Seed,
  24. PUNICODE_STRING String
  25. );
  26. //
  27. // private export from GDI
  28. //
  29. UINT WINAPI QueryFontAssocStatus(void);
  30. #define umin(a, b) \
  31. ((unsigned)(a) < (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
  32. #define umax(a, b) \
  33. ((unsigned)(a) > (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
  34. #define UNICODE_CARRIAGERETURN ((WCHAR)0x0d)
  35. #define UNICODE_LINEFEED ((WCHAR)0x0a)
  36. #define UNICODE_TAB ((WCHAR)0x09)
  37. //
  38. // IME Menu IDs
  39. //
  40. #define ID_IMEOPENCLOSE 10001
  41. #define ID_SOFTKBDOPENCLOSE 10002
  42. #define ID_RECONVERTSTRING 10003
  43. #define ID_EDITTIMER 10007
  44. #define EDIT_TIPTIMEOUT 10000
  45. #pragma code_seg(CODESEG_INIT)
  46. //---------------------------------------------------------------------------//
  47. //
  48. // InitEditClass() - Registers the control's window class
  49. //
  50. BOOL InitEditClass(HINSTANCE hInstance)
  51. {
  52. WNDCLASS wc;
  53. wc.lpfnWndProc = Edit_WndProc;
  54. wc.lpszClassName = WC_EDIT;
  55. wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS;
  56. wc.cbClsExtra = 0;
  57. wc.cbWndExtra = sizeof(PED);
  58. wc.hInstance = hInstance;
  59. wc.hIcon = NULL;
  60. wc.hCursor = LoadCursor(NULL, IDC_IBEAM);
  61. wc.hbrBackground = NULL;
  62. wc.lpszMenuName = NULL;
  63. if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_EDIT, &wc))
  64. {
  65. //ASSERTMSG(0, "Failed to register %s control for %x.%x", WC_EDIT, GetCurrentProcessId(), GetCurrentThreadId());
  66. return FALSE;
  67. }
  68. return TRUE;
  69. }
  70. #pragma code_seg()
  71. //---------------------------------------------------------------------------//
  72. //
  73. PSTR Edit_Lock(PED ped)
  74. {
  75. PSTR ptext = LocalLock(ped->hText);
  76. ped->iLockLevel++;
  77. //
  78. // If this is the first lock of the text and the text is encoded
  79. // decode the text.
  80. //
  81. //TraceMsg(TF_STANDARD, "EDIT: lock : %d '%10s'", ped->iLockLevel, ptext);
  82. if (ped->iLockLevel == 1 && ped->fEncoded)
  83. {
  84. //
  85. // rtlrundecode can't handle zero length strings
  86. //
  87. if (ped->cch != 0)
  88. {
  89. STRING string;
  90. string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
  91. string.Buffer = ptext;
  92. RtlRunDecodeUnicodeString(ped->seed, (PUNICODE_STRING)&string);
  93. //TraceMsg(TF_STANDARD, "EDIT: Decoding: '%10s'", ptext);
  94. }
  95. ped->fEncoded = FALSE;
  96. }
  97. return ptext;
  98. }
  99. //---------------------------------------------------------------------------//
  100. //
  101. VOID Edit_Unlock(PED ped)
  102. {
  103. //
  104. // if we are removing the last lock on the text and the password
  105. // character is set then encode the text
  106. //
  107. //TraceMsg(TF_STANDARD, "EDIT: unlock: %d '%10s'", ped->iLockLevel, ped->ptext);
  108. if (ped->charPasswordChar && ped->iLockLevel == 1 && ped->cch != 0)
  109. {
  110. UNICODE_STRING string;
  111. string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
  112. string.Buffer = LocalLock(ped->hText);
  113. RtlRunEncodeUnicodeString(&(ped->seed), &string);
  114. //TraceMsg(TF_STANDARD, "EDIT: Encoding: '%10s'", ped->ptext);
  115. ped->fEncoded = TRUE;
  116. LocalUnlock(ped->hText);
  117. }
  118. LocalUnlock(ped->hText);
  119. ped->iLockLevel--;
  120. }
  121. //---------------------------------------------------------------------------//
  122. //
  123. // GetActualNegA()
  124. //
  125. // For a given strip of text, this function computes the negative A width
  126. // for the whole strip and returns the value as a postive number.
  127. // It also fills the NegAInfo structure with details about the postion
  128. // of this strip that results in this Negative A.
  129. //
  130. UINT GetActualNegA(HDC hdc, PED ped, INT x, LPSTR lpstring, ICH ichString, INT nCount, LPSTRIPINFO NegAInfo)
  131. {
  132. INT iCharCount, i;
  133. INT iLeftmostPoint = x;
  134. PABC pABCwidthBuff;
  135. UINT wCharIndex;
  136. INT xStartPoint = x;
  137. ABC abc;
  138. //
  139. // To begin with, let us assume that there is no negative A width for
  140. // this strip and initialize accodingly.
  141. //
  142. NegAInfo->XStartPos = x;
  143. NegAInfo->lpString = lpstring;
  144. NegAInfo->nCount = 0;
  145. NegAInfo->ichString = ichString;
  146. //
  147. // If the current font is not a TrueType font, then there can not be any
  148. // negative A widths.
  149. //
  150. if (!ped->fTrueType)
  151. {
  152. if(!ped->charOverhang)
  153. {
  154. return 0;
  155. }
  156. else
  157. {
  158. NegAInfo->nCount = min(nCount, (INT)ped->wMaxNegAcharPos);
  159. return ped->charOverhang;
  160. }
  161. }
  162. //
  163. // How many characters are to be considered for computing Negative A ?
  164. //
  165. iCharCount = min(nCount, (INT)ped->wMaxNegAcharPos);
  166. //
  167. // Do we have the info on individual character's widths?
  168. //
  169. if(!ped->charWidthBuffer)
  170. {
  171. //
  172. // No! So, let us tell them to consider all the characters.
  173. //
  174. NegAInfo->nCount = iCharCount;
  175. return (iCharCount * ped->aveCharWidth);
  176. }
  177. pABCwidthBuff = (PABC)ped->charWidthBuffer;
  178. if (ped->fAnsi)
  179. {
  180. for (i = 0; i < iCharCount; i++)
  181. {
  182. wCharIndex = (UINT)(*((PUCHAR)lpstring));
  183. if (*lpstring == VK_TAB)
  184. {
  185. //
  186. // To play it safe, we assume that this tab results in a tab length of
  187. // 1 pixel because this is the minimum possible tab length.
  188. //
  189. x++;
  190. }
  191. else
  192. {
  193. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  194. {
  195. //
  196. // Add the 'A' width.
  197. //
  198. x += pABCwidthBuff[wCharIndex].abcA;
  199. }
  200. else
  201. {
  202. GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc);
  203. x += abc.abcA;
  204. }
  205. if (x < iLeftmostPoint)
  206. {
  207. //
  208. // Reset the leftmost point.
  209. //
  210. iLeftmostPoint = x;
  211. }
  212. if (x < xStartPoint)
  213. {
  214. //
  215. // 'i' is index; To get the count add 1.
  216. //
  217. NegAInfo->nCount = i+1;
  218. }
  219. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  220. {
  221. x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC;
  222. }
  223. else
  224. {
  225. x += abc.abcB + abc.abcC;
  226. }
  227. }
  228. lpstring++;
  229. }
  230. }
  231. else
  232. {
  233. LPWSTR lpwstring = (LPWSTR)lpstring;
  234. for (i = 0; i < iCharCount; i++)
  235. {
  236. wCharIndex = *lpwstring ;
  237. if (*lpwstring == VK_TAB)
  238. {
  239. //
  240. // To play it safe, we assume that this tab results in a tab length of
  241. // 1 pixel because this is the minimum possible tab length.
  242. //
  243. x++;
  244. }
  245. else
  246. {
  247. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  248. {
  249. //
  250. // Add the 'A' width.
  251. //
  252. x += pABCwidthBuff[wCharIndex].abcA;
  253. }
  254. else
  255. {
  256. GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc);
  257. x += abc.abcA ;
  258. }
  259. if (x < iLeftmostPoint)
  260. {
  261. //
  262. // Reset the leftmost point.
  263. //
  264. iLeftmostPoint = x;
  265. }
  266. if (x < xStartPoint)
  267. {
  268. //
  269. // 'i' is index; To get the count add 1.
  270. //
  271. NegAInfo->nCount = i+1;
  272. }
  273. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  274. {
  275. x += pABCwidthBuff[wCharIndex].abcB +
  276. pABCwidthBuff[wCharIndex].abcC;
  277. }
  278. else
  279. {
  280. x += abc.abcB + abc.abcC;
  281. }
  282. }
  283. lpwstring++;
  284. }
  285. }
  286. //
  287. // Let us return the negative A for the whole strip as a positive value.
  288. //
  289. return (UINT)(xStartPoint - iLeftmostPoint);
  290. }
  291. //---------------------------------------------------------------------------//
  292. //
  293. // Edit_IsAncestorActive()
  294. //
  295. // Returns whether or not we're the child of an "active" window. Looks for
  296. // the first parent window that has a caption.
  297. //
  298. // This is a function because we might use it elsewhere when getting left
  299. // clicked on, etc.
  300. //
  301. BOOL Edit_IsAncestorActive(HWND hwnd)
  302. {
  303. BOOL fResult = TRUE;
  304. //
  305. // We want to return TRUE always for top level windows. That's because
  306. // of how WM_MOUSEACTIVATE works. If we see the click at all, the
  307. // window is active. However, if we reach a child ancestor that has
  308. // a caption, return the frame-on style bit.
  309. //
  310. // Note that calling FlashWindow() will have an effect. If the user
  311. // clicks on an edit field in a child window that is flashed off, nothing
  312. // will happen unless the window stops flashing and ncactivates first.
  313. //
  314. for(; hwnd != NULL; hwnd = GetParent(hwnd))
  315. {
  316. PWW pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
  317. //
  318. // Bail out if some parent window isn't 4.0 compatible or we've
  319. // reached the top. Fixes compatibility problems with 3.x apps,
  320. // especially MFC samples.
  321. //
  322. if (!TESTFLAG(pww->dwState2, WS_S2_WIN40COMPAT) || !TESTFLAG(pww->dwStyle, WS_CHILD))
  323. {
  324. break;
  325. }
  326. else if (TESTFLAG(pww->dwState, WS_ST_CPRESENT))
  327. {
  328. fResult = (TESTFLAG(pww->dwState, WS_ST_FRAMEON) != 0);
  329. break;
  330. }
  331. }
  332. return fResult;
  333. }
  334. //---------------------------------------------------------------------------//
  335. //
  336. // Edit_SetIMEMenu()
  337. //
  338. // support IME specific context menu
  339. //
  340. BOOL Edit_SetIMEMenu(HMENU hMenu, HWND hwnd, EditMenuItemState state)
  341. {
  342. MENUITEMINFO mii;
  343. HIMC hIMC;
  344. HKL hKL;
  345. HMENU hmenuSub;
  346. WCHAR szRes[32];
  347. INT nPrevLastItem;
  348. INT nItemsAdded = 0;
  349. UserAssert(g_fIMMEnabled && state.fIME);
  350. hKL = GetKeyboardLayout(0);
  351. if (!ImmIsIME(hKL))
  352. {
  353. return TRUE;
  354. }
  355. hIMC = ImmGetContext(hwnd);
  356. if (hIMC == NULL)
  357. {
  358. //
  359. // early out
  360. //
  361. return FALSE;
  362. }
  363. hmenuSub = GetSubMenu(hMenu, 0);
  364. if (hmenuSub == NULL)
  365. {
  366. return FALSE;
  367. }
  368. nPrevLastItem = GetMenuItemCount(hmenuSub);
  369. if (hIMC)
  370. {
  371. if (LOWORD(HandleToUlong(hKL)) != 0x412)
  372. {
  373. //
  374. // If Korean, do not show open/close menus
  375. //
  376. if (ImmGetOpenStatus(hIMC))
  377. {
  378. LoadString(HINST_THISDLL, IDS_IMECLOSE, szRes, ARRAYSIZE(szRes));
  379. }
  380. else
  381. {
  382. LoadString(HINST_THISDLL, IDS_IMEOPEN, szRes, ARRAYSIZE(szRes));
  383. }
  384. mii.cbSize = sizeof(MENUITEMINFO);
  385. mii.fMask = MIIM_STRING | MIIM_ID;
  386. mii.dwTypeData = szRes;
  387. mii.cch = 0xffff;
  388. mii.wID = ID_IMEOPENCLOSE;
  389. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  390. ++nItemsAdded;
  391. }
  392. if (ImmGetProperty(hKL, IGP_CONVERSION) & IME_CMODE_SOFTKBD)
  393. {
  394. DWORD fdwConversion;
  395. if (ImmGetConversionStatus(hIMC, &fdwConversion, NULL) &&
  396. (fdwConversion & IME_CMODE_SOFTKBD))
  397. {
  398. LoadString(HINST_THISDLL, IDS_SOFTKBDCLOSE, szRes, ARRAYSIZE(szRes));
  399. }
  400. else
  401. {
  402. LoadString(HINST_THISDLL, IDS_SOFTKBDOPEN, szRes, ARRAYSIZE(szRes));
  403. }
  404. mii.cbSize = sizeof(MENUITEMINFO);
  405. mii.fMask = MIIM_STRING | MIIM_ID;
  406. mii.dwTypeData = szRes;
  407. mii.cch = 0xffff;
  408. mii.wID = ID_SOFTKBDOPENCLOSE;
  409. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  410. ++nItemsAdded;
  411. }
  412. if (LOWORD(HandleToUlong(hKL)) != 0x412)
  413. {
  414. //
  415. // If Korean, do not show reconversion menus
  416. //
  417. DWORD dwSCS = ImmGetProperty(hKL, IGP_SETCOMPSTR);
  418. LoadString(HINST_THISDLL, IDS_RECONVERTSTRING, szRes, ARRAYSIZE(szRes));
  419. mii.cbSize = sizeof(MENUITEMINFO);
  420. mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE;
  421. mii.dwTypeData = szRes;
  422. mii.fState = 0;
  423. mii.cch = 0xffff;
  424. mii.wID = ID_RECONVERTSTRING;
  425. if (state.fDisableCut ||
  426. !(dwSCS & SCS_CAP_SETRECONVERTSTRING) ||
  427. !(dwSCS & SCS_CAP_MAKEREAD))
  428. {
  429. mii.fState |= MFS_GRAYED;
  430. }
  431. InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
  432. ++nItemsAdded;
  433. }
  434. }
  435. //
  436. // Add or remove the menu separator
  437. //
  438. if (state.fNeedSeparatorBeforeImeMenu && nItemsAdded != 0)
  439. {
  440. //
  441. // If the menu for Middle East has left a separator,
  442. // fNeedSeparatorBeforeImeMenu is FALSE.
  443. // I.e. we don't need to add more.
  444. //
  445. mii.cbSize = sizeof(MENUITEMINFO);
  446. mii.fMask = MIIM_FTYPE;
  447. mii.fType = MFT_SEPARATOR;
  448. InsertMenuItem(hmenuSub, nPrevLastItem, TRUE, &mii);
  449. }
  450. else if (!state.fNeedSeparatorBeforeImeMenu && nItemsAdded == 0)
  451. {
  452. //
  453. // Extra separator is left by ME menus. Remove it.
  454. //
  455. DeleteMenu(hmenuSub, nPrevLastItem - 1, MF_BYPOSITION);
  456. }
  457. ImmReleaseContext(hwnd, hIMC);
  458. return TRUE;
  459. }
  460. //---------------------------------------------------------------------------//
  461. //
  462. VOID Edit_InOutReconversionMode(PED ped, BOOL fIn)
  463. {
  464. UserAssert(fIn == TRUE || fIn == FALSE);
  465. if (fIn != ped->fInReconversion)
  466. {
  467. ped->fInReconversion = fIn;
  468. if (ped->fFocus)
  469. {
  470. (fIn ? HideCaret: ShowCaret)(ped->hwnd);
  471. }
  472. }
  473. }
  474. //---------------------------------------------------------------------------//
  475. //
  476. BOOL Edit_EnumInputContextCB(HIMC hImc, LPARAM lParam)
  477. {
  478. DWORD dwConversion = 0, dwSentence = 0, dwNewConversion = 0;
  479. ImmGetConversionStatus(hImc, &dwConversion, &dwSentence);
  480. if (lParam)
  481. {
  482. dwNewConversion = dwConversion | IME_CMODE_SOFTKBD;
  483. }
  484. else
  485. {
  486. dwNewConversion = dwConversion & ~IME_CMODE_SOFTKBD;
  487. }
  488. if (dwNewConversion != dwConversion)
  489. {
  490. ImmSetConversionStatus(hImc, dwNewConversion, dwSentence);
  491. }
  492. return TRUE;
  493. }
  494. //---------------------------------------------------------------------------//
  495. //
  496. // Edit_DoIMEMenuCommand()
  497. //
  498. // support IME specific context menu
  499. //
  500. BOOL Edit_DoIMEMenuCommand(PED ped, int cmd, HWND hwnd)
  501. {
  502. HIMC hIMC;
  503. // early out
  504. switch (cmd)
  505. {
  506. case ID_IMEOPENCLOSE:
  507. case ID_SOFTKBDOPENCLOSE:
  508. case ID_RECONVERTSTRING:
  509. break;
  510. default:
  511. return FALSE;
  512. }
  513. //
  514. // everybody needs hIMC, so get it here
  515. //
  516. hIMC = ImmGetContext(hwnd);
  517. if (hIMC == NULL)
  518. {
  519. //
  520. // indicate to caller, that no further command processing needed
  521. //
  522. return TRUE;
  523. }
  524. switch (cmd)
  525. {
  526. case ID_IMEOPENCLOSE:
  527. {
  528. // switch IME Open/Close status
  529. BOOL fOpen = ImmGetOpenStatus(hIMC);
  530. ImmSetOpenStatus(hIMC, !fOpen);
  531. }
  532. break;
  533. case ID_SOFTKBDOPENCLOSE:
  534. {
  535. DWORD fdwConversion;
  536. if (ImmGetConversionStatus(hIMC, &fdwConversion, NULL))
  537. {
  538. //
  539. // Toggle soft keyboard Open/Close status
  540. //
  541. ImmEnumInputContext(0, Edit_EnumInputContextCB,
  542. (fdwConversion & IME_CMODE_SOFTKBD) != IME_CMODE_SOFTKBD);
  543. }
  544. }
  545. break;
  546. case ID_RECONVERTSTRING:
  547. {
  548. DWORD dwStrLen; // holds TCHAR count of recionversion string
  549. DWORD cbLen; // holds BYTE SIZE of reconversion string
  550. DWORD dwSize;
  551. LPRECONVERTSTRING lpRCS;
  552. //
  553. // pass current selection to IME for reconversion
  554. //
  555. dwStrLen = ped->ichMaxSel - ped->ichMinSel;
  556. cbLen = dwStrLen * ped->cbChar;
  557. dwSize = cbLen + sizeof(RECONVERTSTRING) + 8;
  558. lpRCS = (LPRECONVERTSTRING)UserLocalAlloc(0, dwSize);
  559. if (lpRCS)
  560. {
  561. LPBYTE pText;
  562. ICH ichSelMinOrg;
  563. ichSelMinOrg = ped->ichMinSel;
  564. pText = Edit_Lock(ped);
  565. if (pText != NULL)
  566. {
  567. LPBYTE lpDest;
  568. BOOL (WINAPI* fpSetCompositionStringAW)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD);
  569. lpRCS->dwSize = dwSize;
  570. lpRCS->dwVersion = 0;
  571. lpRCS->dwStrLen =
  572. lpRCS->dwCompStrLen =
  573. lpRCS->dwTargetStrLen = dwStrLen;
  574. lpRCS->dwStrOffset = sizeof(RECONVERTSTRING);
  575. lpRCS->dwCompStrOffset =
  576. lpRCS->dwTargetStrOffset = 0;
  577. lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING);
  578. RtlCopyMemory(lpDest, pText + ped->ichMinSel * ped->cbChar, cbLen);
  579. if (ped->fAnsi)
  580. {
  581. LPBYTE psz = (LPBYTE)lpDest;
  582. psz[cbLen] = '\0';
  583. fpSetCompositionStringAW = ImmSetCompositionStringA;
  584. }
  585. else
  586. {
  587. LPWSTR pwsz = (LPWSTR)lpDest;
  588. pwsz[dwStrLen] = L'\0';
  589. fpSetCompositionStringAW = ImmSetCompositionStringW;
  590. }
  591. Edit_Unlock(ped);
  592. UserAssert(fpSetCompositionStringAW != NULL);
  593. Edit_InOutReconversionMode(ped, TRUE);
  594. Edit_ImmSetCompositionWindow(ped, 0, 0); // x and y will be overriden anyway
  595. // Query the IME for a valid Reconvert string range first.
  596. fpSetCompositionStringAW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
  597. // If current IME updates the original reconvert structure,
  598. // it is necessary to update the text selection based on the
  599. // new reconvert text range.
  600. if ((lpRCS->dwCompStrLen != dwStrLen) || (ichSelMinOrg != ped->ichMinSel))
  601. {
  602. ICH ichSelStart;
  603. ICH ichSelEnd;
  604. ichSelStart = ichSelMinOrg + (lpRCS->dwCompStrOffset / ped->cbChar);
  605. ichSelEnd = ichSelStart + lpRCS->dwCompStrLen;
  606. (ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd);
  607. }
  608. fpSetCompositionStringAW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
  609. }
  610. UserLocalFree(lpRCS);
  611. }
  612. break;
  613. }
  614. default:
  615. //
  616. // should never reach here.
  617. //
  618. TraceMsg(TF_STANDARD, "EDIT: Edit_DoIMEMenuCommand: unknown command id %d; should never reach here.", cmd);
  619. return FALSE;
  620. }
  621. UserAssert(hIMC != NULL);
  622. ImmReleaseContext(hwnd, hIMC);
  623. return TRUE;
  624. }
  625. //---------------------------------------------------------------------------//
  626. //
  627. // Edit_Menu()
  628. //
  629. // Handles context menu for edit fields. Disables inappropriate commands.
  630. // Note that this is NOT subclassing friendly, like most of our functions,
  631. // for speed and convenience.
  632. //
  633. VOID Edit_Menu(HWND hwnd, PED ped, LPPOINT pt)
  634. {
  635. HMENU hMenu;
  636. INT cmd = 0;
  637. INT x;
  638. INT y;
  639. UINT uFlags = TPM_NONOTIFY | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON;
  640. EditMenuItemState state =
  641. {
  642. FALSE, // fDisableCut
  643. TRUE, // fDisablePaste
  644. TRUE, // fNeedSeparatorBeforeImeMenu
  645. g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0)), // fIME
  646. };
  647. //
  648. // Set focus if we don't have its
  649. //
  650. if (!ped->fFocus)
  651. {
  652. SetFocus(hwnd);
  653. }
  654. //
  655. // Grab the menu from our resources...
  656. //
  657. hMenu = LoadMenu( HINST_THISDLL, MAKEINTRESOURCE( ID_EC_PROPERTY_MENU ));
  658. if (hMenu)
  659. {
  660. //
  661. // Undo -- not allowed if we have no saved undo info
  662. //
  663. if (ped->undoType == UNDO_NONE)
  664. {
  665. EnableMenuItem(hMenu, WM_UNDO, MF_BYCOMMAND | MFS_GRAYED);
  666. }
  667. if (ped->fReadOnly || ped->charPasswordChar)
  668. {
  669. //
  670. // Cut and Delete -- not allowed if read-only or password
  671. //
  672. state.fDisableCut = TRUE;
  673. }
  674. else
  675. {
  676. //
  677. // Cut, Delete -- not allowed if there's no selection
  678. //
  679. if (ped->ichMinSel == ped->ichMaxSel)
  680. {
  681. state.fDisableCut = TRUE;
  682. }
  683. }
  684. //
  685. // Paste -- not allowed if there's no text on the clipboard
  686. // (this works for both OEM and Unicode)
  687. // Used to be always disabled for password edits MCostea #221035
  688. //
  689. if (IsClipboardFormatAvailable(CF_TEXT))
  690. {
  691. state.fDisablePaste = FALSE;
  692. }
  693. if (state.fDisableCut)
  694. {
  695. EnableMenuItem(hMenu, WM_CUT, MF_BYCOMMAND | MFS_GRAYED);
  696. EnableMenuItem(hMenu, WM_CLEAR, MF_BYCOMMAND | MFS_GRAYED);
  697. }
  698. if (state.fDisablePaste)
  699. {
  700. EnableMenuItem(hMenu, WM_PASTE, MF_BYCOMMAND | MFS_GRAYED);
  701. }
  702. //
  703. // Copy -- not allowed if there's no selection or password ec
  704. //
  705. if ((ped->ichMinSel == ped->ichMaxSel) || (ped->charPasswordChar))
  706. {
  707. EnableMenuItem(hMenu, WM_COPY, MF_BYCOMMAND | MFS_GRAYED);
  708. }
  709. //
  710. // Select All -- not allowed if there's no text or if everything is
  711. // selected. Latter case takes care of first one.
  712. //
  713. if ((ped->ichMinSel == 0) && (ped->ichMaxSel == ped->cch))
  714. {
  715. EnableMenuItem(hMenu, EM_SETSEL, MF_BYCOMMAND | MFS_GRAYED);
  716. }
  717. if (ped->pLpkEditCallout)
  718. {
  719. ped->pLpkEditCallout->EditSetMenu((PED0)ped, hMenu);
  720. }
  721. else
  722. {
  723. DeleteMenu(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND);
  724. DeleteMenu(hMenu, ID_CNTX_RTL, MF_BYCOMMAND);
  725. DeleteMenu(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND);
  726. if (state.fIME)
  727. {
  728. //
  729. // One separator is left in the menu,
  730. // no need to add the one before IME menus
  731. //
  732. state.fNeedSeparatorBeforeImeMenu = FALSE;
  733. }
  734. else
  735. {
  736. //
  737. // Extra separator is left. Remove it.
  738. //
  739. HMENU hmenuSub = GetSubMenu(hMenu, 0);
  740. INT nItems = GetMenuItemCount(hmenuSub) - 1;
  741. UserAssert(nItems >= 0);
  742. UserAssert(GetMenuState(hmenuSub, nItems, MF_BYPOSITION) & MF_SEPARATOR);
  743. //
  744. // remove needless separator
  745. //
  746. DeleteMenu(hmenuSub, nItems, MF_BYPOSITION);
  747. }
  748. }
  749. //
  750. // IME specific menu
  751. //
  752. if (state.fIME)
  753. {
  754. Edit_SetIMEMenu(hMenu, hwnd, state);
  755. }
  756. //
  757. // BOGUS
  758. // We position the menu below & to the right of the point clicked on.
  759. // Is this cool? I think so. Excel 4.0 does the same thing. It
  760. // seems like it would be neat if we could avoid obscuring the
  761. // selection. But in actuality, it seems even more awkward to move
  762. // the menu out of the way of the selection. The user can't click
  763. // and drag that way, and they have to move the mouse a ton.
  764. //
  765. // We need to use TPM_NONOTIFY because VBRUN100 and VBRUN200 GP-fault
  766. // on unexpected menu messages.
  767. //
  768. //
  769. // if message came via the keyboard then center on the control
  770. // We use -1 && -1 here not 0xFFFFFFFF like Win95 becuase we
  771. // previously converted the lParam to a point with sign extending.
  772. //
  773. if (pt->x == -1 && pt->y == -1)
  774. {
  775. RECT rc;
  776. GetWindowRect(hwnd, &rc);
  777. x = rc.left + (rc.right - rc.left) / 2;
  778. y = rc.top + (rc.bottom - rc.top) / 2;
  779. }
  780. else
  781. {
  782. x = pt->x;
  783. y = pt->y;
  784. }
  785. if ( IS_BIDI_LOCALIZED_SYSTEM() )
  786. {
  787. uFlags |= TPM_LAYOUTRTL;
  788. }
  789. cmd = TrackPopupMenuEx(GetSubMenu(hMenu, 0), uFlags, x, y, hwnd, NULL);
  790. //
  791. // Free our menu
  792. //
  793. DestroyMenu(hMenu);
  794. if (cmd && (cmd != -1))
  795. {
  796. if (ped->pLpkEditCallout && cmd)
  797. {
  798. ped->pLpkEditCallout->EditProcessMenu((PED0)ped, cmd);
  799. }
  800. if (!state.fIME || !Edit_DoIMEMenuCommand(ped, cmd, hwnd))
  801. {
  802. //
  803. // if cmd is not IME specific menu, send it.
  804. //
  805. SendMessage(hwnd, cmd, 0, (cmd == EM_SETSEL) ? 0xFFFFFFFF : 0L );
  806. }
  807. }
  808. }
  809. }
  810. //---------------------------------------------------------------------------//
  811. //
  812. // Edit_ClearText()
  813. //
  814. // Clears selected text. Does NOT _send_ a fake char backspace.
  815. //
  816. VOID Edit_ClearText(PED ped)
  817. {
  818. if (!ped->fReadOnly && (ped->ichMinSel < ped->ichMaxSel))
  819. {
  820. if (ped->fSingle)
  821. {
  822. EditSL_WndProc(ped, WM_CHAR, VK_BACK, 0L );
  823. }
  824. else
  825. {
  826. EditML_WndProc(ped, WM_CHAR, VK_BACK, 0L );
  827. }
  828. }
  829. }
  830. //---------------------------------------------------------------------------//
  831. //
  832. // Edit_CutText()
  833. //
  834. // Cuts selected text. This removes and copies the selection to the clip,
  835. // or if nothing is selected we delete (clear) the left character.
  836. //
  837. VOID Edit_CutText(PED ped)
  838. {
  839. //
  840. // Cut selection--IE, remove and copy to clipboard, or if no selection,
  841. // delete (clear) character left.
  842. //
  843. if (!ped->fReadOnly &&
  844. (ped->ichMinSel < ped->ichMaxSel) &&
  845. SendMessage(ped->hwnd, WM_COPY, 0, 0L))
  846. {
  847. //
  848. // If copy was successful, delete the copied text by sending a
  849. // backspace message which will redraw the text and take care of
  850. // notifying the parent of changes.
  851. //
  852. Edit_ClearText(ped);
  853. }
  854. }
  855. //---------------------------------------------------------------------------//
  856. //
  857. // Edit_GetModKeys()
  858. //
  859. // Gets modifier key states. Currently, we only check for VK_CONTROL and
  860. // VK_SHIFT.
  861. //
  862. INT Edit_GetModKeys(INT keyMods)
  863. {
  864. INT scState;
  865. scState = 0;
  866. if (!keyMods)
  867. {
  868. if (GetKeyState(VK_CONTROL) < 0)
  869. {
  870. scState |= CTRLDOWN;
  871. }
  872. if (GetKeyState(VK_SHIFT) < 0)
  873. {
  874. scState |= SHFTDOWN;
  875. }
  876. }
  877. else if (keyMods != NOMODIFY)
  878. {
  879. scState = keyMods;
  880. }
  881. return scState;
  882. }
  883. //---------------------------------------------------------------------------//
  884. //
  885. // Edit_TabTheTextOut() AorW
  886. // If fDrawText == FALSE, then this function returns the text extent of
  887. // of the given strip of text. It does not worry about the Negative widths.
  888. //
  889. // If fDrawText == TRUE, this draws the given strip of Text expanding the
  890. // tabs to proper lengths, calculates and fills up the NegCInfoForStrip with
  891. // details required to draw the portions of this strip that goes beyond the
  892. // xClipEndPos due to Negative C widths.
  893. //
  894. // Returns the max width AS A DWORD. We don't care about the height
  895. // at all. No one uses it. We keep a DWORD because that way we avoid
  896. // overflow.
  897. //
  898. // NOTE: If the language pack is loaded EcTabTheTextOut is not used - the
  899. // language pack must take care of all tab expansion and selection
  900. // highlighting with full support for bidi layout and complex script
  901. // glyph reordering.
  902. //
  903. UINT Edit_TabTheTextOut(
  904. HDC hdc,
  905. INT xClipStPos,
  906. INT xClipEndPos,
  907. INT xStart,
  908. INT y,
  909. LPSTR lpstring,
  910. INT nCount,
  911. ICH ichString,
  912. PED ped,
  913. INT iTabOrigin,
  914. BOOL fDraw,
  915. LPSTRIPINFO NegCInfoForStrip)
  916. {
  917. INT nTabPositions; // Count of tabstops in tabstop array.
  918. LPINT lpintTabStopPositions; // Tab stop positions in pixels.
  919. INT cch;
  920. UINT textextent;
  921. INT xEnd;
  922. INT pixeltabstop = 0;
  923. INT i;
  924. INT cxCharWidth;
  925. RECT rc;
  926. BOOL fOpaque;
  927. BOOL fFirstPass = TRUE;
  928. PINT charWidthBuff;
  929. INT iTabLength;
  930. INT nConsecutiveTabs;
  931. INT xStripStPos;
  932. INT xStripEndPos;
  933. INT xEndOfStrip;
  934. STRIPINFO RedrawStripInfo;
  935. STRIPINFO NegAInfo;
  936. LPSTR lpTab;
  937. LPWSTR lpwTab;
  938. UINT wNegCwidth, wNegAwidth;
  939. INT xRightmostPoint = xClipStPos;
  940. INT xTabStartPos;
  941. INT iSavedBkMode = 0;
  942. WCHAR wchar;
  943. SIZE size = {0};
  944. ABC abc ;
  945. COLORREF clrBkSave;
  946. COLORREF clrTextSave;
  947. HBRUSH hbrBack = NULL;
  948. BOOL fNeedDelete = FALSE;
  949. HRESULT hr = E_FAIL;
  950. UINT uRet;
  951. //
  952. // Algorithm: Draw the strip opaquely first. If a tab length is so
  953. // small that the portions of text on either side of a tab overlap with
  954. // the other, then this will result in some clipping. So, such portion
  955. // of the strip is remembered in "RedrawStripInfo" and redrawn
  956. // transparently later to compensate the clippings.
  957. // NOTE: "RedrawStripInfo" can hold info about just one portion. So, if
  958. // more than one portion of the strip needs to be redrawn transparently,
  959. // then we "merge" all such portions into a single strip and redraw that
  960. // strip at the end.
  961. //
  962. if (fDraw)
  963. {
  964. //
  965. // To begin with, let us assume that there is no Negative C for this
  966. // strip and initialize the Negative Width Info structure.
  967. //
  968. NegCInfoForStrip->nCount = 0;
  969. NegCInfoForStrip->XStartPos = xClipEndPos;
  970. //
  971. // We may not have to redraw any portion of this strip.
  972. //
  973. RedrawStripInfo.nCount = 0;
  974. fOpaque = (GetBkMode(hdc) == OPAQUE) || (fDraw == ECT_SELECTED);
  975. }
  976. #if DBG
  977. else
  978. {
  979. //
  980. // Both EditML_GetLineWidth() and Edit_CchInWidth() should be clipping
  981. // nCount to avoid overflow.
  982. //
  983. if (nCount > MAXLINELENGTH)
  984. {
  985. TraceMsg(TF_STANDARD, "EDIT: Edit_TabTheTextOut: %d > MAXLINELENGTH", nCount);
  986. }
  987. }
  988. #endif
  989. //
  990. // Let us define the Clip rectangle.
  991. //
  992. rc.left = xClipStPos;
  993. rc.right = xClipEndPos;
  994. rc.top = y;
  995. rc.bottom = y + ped->lineHeight;
  996. #ifdef _USE_DRAW_THEME_TEXT_
  997. //
  998. // Check if we are themed.
  999. //
  1000. if (ped->hTheme)
  1001. {
  1002. COLORREF clrBk;
  1003. COLORREF clrText;
  1004. INT iState;
  1005. INT iProp;
  1006. iState = (fDraw == ECT_SELECTED) ? ETS_SELECTED : Edit_GetStateId(ped);
  1007. iProp = (fDraw == ECT_SELECTED) ? TMT_HIGHLIGHT : TMT_FILLCOLOR;
  1008. hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrBk);
  1009. if (SUCCEEDED(hr))
  1010. {
  1011. iProp = (fDraw == ECT_SELECTED) ? TMT_HIGHLIGHTTEXT : TMT_TEXTCOLOR;
  1012. hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrText);
  1013. if (SUCCEEDED(hr))
  1014. {
  1015. hbrBack = CreateSolidBrush(clrBk);
  1016. fNeedDelete = TRUE;
  1017. clrBkSave = SetBkColor(hdc, clrBk);
  1018. clrTextSave = SetTextColor(hdc, clrText);
  1019. }
  1020. }
  1021. }
  1022. #endif // _USE_DRAW_THEME_TEXT_
  1023. if (!ped->hTheme || FAILED(hr))
  1024. {
  1025. if (fDraw == ECT_SELECTED)
  1026. {
  1027. //
  1028. // use normal colors
  1029. //
  1030. hbrBack = GetSysColorBrush(COLOR_HIGHLIGHT);
  1031. clrBkSave = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  1032. clrTextSave = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  1033. }
  1034. else
  1035. {
  1036. hbrBack = Edit_GetBrush(ped, hdc, &fNeedDelete);
  1037. clrBkSave = GetBkColor(hdc);
  1038. clrTextSave = GetTextColor(hdc);
  1039. }
  1040. }
  1041. //
  1042. // Check if anything needs to be drawn.
  1043. //
  1044. if (!lpstring || !nCount)
  1045. {
  1046. if (fDraw)
  1047. {
  1048. ExtTextOutW(hdc, xClipStPos, y,
  1049. (fOpaque ? ETO_OPAQUE | ETO_CLIPPED : ETO_CLIPPED),
  1050. &rc, L"", 0, 0L);
  1051. }
  1052. uRet = 0;
  1053. }
  1054. else
  1055. {
  1056. //
  1057. // Starting position
  1058. //
  1059. xEnd = xStart;
  1060. cxCharWidth = ped->aveCharWidth;
  1061. nTabPositions = (ped->pTabStops ? *(ped->pTabStops) : 0);
  1062. if (ped->pTabStops)
  1063. {
  1064. lpintTabStopPositions = (LPINT)(ped->pTabStops+1);
  1065. if (nTabPositions == 1)
  1066. {
  1067. pixeltabstop = lpintTabStopPositions[0];
  1068. if (!pixeltabstop)
  1069. {
  1070. pixeltabstop = 1;
  1071. }
  1072. }
  1073. }
  1074. else
  1075. {
  1076. lpintTabStopPositions = NULL;
  1077. pixeltabstop = 8*cxCharWidth;
  1078. }
  1079. //
  1080. // The first time we will draw the strip Opaquely. If some portions need
  1081. // to be redrawn , then we will set the mode to TRANSPARENT and
  1082. // jump to this location to redraw those portions.
  1083. //
  1084. RedrawStrip:
  1085. while (nCount)
  1086. {
  1087. wNegCwidth = ped->wMaxNegC;
  1088. //
  1089. // Search for the first TAB in this strip; also compute the extent
  1090. // of the the strip upto and not including the tab character.
  1091. //
  1092. // Note - If the langpack is loaded, there will be no charWidthBuffer.
  1093. //
  1094. //
  1095. // Do we have a character width buffer?
  1096. //
  1097. if (ped->charWidthBuffer)
  1098. {
  1099. textextent = 0;
  1100. cch = nCount;
  1101. //
  1102. // If so, does it have ABC widths?
  1103. //
  1104. if (ped->fTrueType)
  1105. {
  1106. UINT iRightmostPoint = 0;
  1107. UINT wCharIndex;
  1108. PABC pABCwidthBuff;
  1109. pABCwidthBuff = (PABC) ped->charWidthBuffer;
  1110. if (ped->fAnsi)
  1111. {
  1112. for (i = 0; i < nCount; i++)
  1113. {
  1114. if (lpstring[i] == VK_TAB)
  1115. {
  1116. cch = i;
  1117. break;
  1118. }
  1119. wCharIndex = (UINT)(((PUCHAR)lpstring)[i]);
  1120. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  1121. {
  1122. textextent += (UINT)(pABCwidthBuff[wCharIndex].abcA +
  1123. pABCwidthBuff[wCharIndex].abcB);
  1124. }
  1125. else
  1126. {
  1127. //
  1128. // not in cache, will ask driver
  1129. //
  1130. GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc);
  1131. textextent += abc.abcA + abc.abcB ;
  1132. }
  1133. if (textextent > iRightmostPoint)
  1134. {
  1135. iRightmostPoint = textextent;
  1136. }
  1137. if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH)
  1138. {
  1139. textextent += pABCwidthBuff[wCharIndex].abcC;
  1140. }
  1141. else
  1142. {
  1143. //
  1144. // not in cache
  1145. //
  1146. textextent += abc.abcC;
  1147. }
  1148. if (textextent > iRightmostPoint)
  1149. {
  1150. iRightmostPoint = textextent;
  1151. }
  1152. }
  1153. }
  1154. else
  1155. {
  1156. for (i = 0; i < nCount; i++)
  1157. {
  1158. WCHAR UNALIGNED * lpwstring = (WCHAR UNALIGNED *)lpstring;
  1159. if (lpwstring[i] == VK_TAB)
  1160. {
  1161. cch = i;
  1162. break;
  1163. }
  1164. wCharIndex = lpwstring[i] ;
  1165. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  1166. {
  1167. textextent += pABCwidthBuff[wCharIndex].abcA +
  1168. pABCwidthBuff[wCharIndex].abcB;
  1169. }
  1170. else
  1171. {
  1172. GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ;
  1173. textextent += abc.abcA + abc.abcB ;
  1174. }
  1175. //
  1176. // Note that abcC could be negative so we need this
  1177. // statement here *and* below
  1178. //
  1179. if (textextent > iRightmostPoint)
  1180. {
  1181. iRightmostPoint = textextent;
  1182. }
  1183. if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
  1184. {
  1185. textextent += pABCwidthBuff[wCharIndex].abcC;
  1186. }
  1187. else
  1188. {
  1189. textextent += abc.abcC ;
  1190. }
  1191. if (textextent > iRightmostPoint)
  1192. {
  1193. iRightmostPoint = textextent;
  1194. }
  1195. }
  1196. }
  1197. wNegCwidth = (int)(iRightmostPoint - textextent);
  1198. }
  1199. else
  1200. {
  1201. //
  1202. // No! This is not a TrueType font; So, we have only character
  1203. // width info in this buffer.
  1204. //
  1205. charWidthBuff = ped->charWidthBuffer;
  1206. if (ped->fAnsi)
  1207. {
  1208. //
  1209. // Initially assume no tabs exist in the text so cch=nCount.
  1210. //
  1211. for (i = 0; i < nCount; i++)
  1212. {
  1213. if (lpstring[i] == VK_TAB)
  1214. {
  1215. cch = i;
  1216. break;
  1217. }
  1218. //
  1219. // Call GetTextExtentPoint for dbcs/hankaku characters
  1220. //
  1221. if (ped->fDBCS && (i+1 < nCount)
  1222. && Edit_IsDBCSLeadByte(ped,lpstring[i]))
  1223. {
  1224. GetTextExtentPointA(hdc, &lpstring[i], 2, &size);
  1225. textextent += size.cx;
  1226. i++;
  1227. }
  1228. else if ((UCHAR)lpstring[i] >= CHAR_WIDTH_BUFFER_LENGTH)
  1229. {
  1230. //
  1231. // Skip this GetExtentPoint call for non hankaku code points
  1232. // Or if the character is in the width cache.
  1233. //
  1234. GetTextExtentPointA(hdc, &lpstring[i], 1, &size);
  1235. textextent += size.cx;
  1236. }
  1237. else
  1238. {
  1239. textextent += (UINT)(charWidthBuff[(UINT)(((PUCHAR)lpstring)[i])]);
  1240. }
  1241. }
  1242. }
  1243. else
  1244. {
  1245. LPWSTR lpwstring = (LPWSTR) lpstring ;
  1246. INT cchUStart; // start of unicode character count
  1247. for (i = 0; i < nCount; i++)
  1248. {
  1249. if (lpwstring[i] == VK_TAB)
  1250. {
  1251. cch = i;
  1252. break;
  1253. }
  1254. wchar = lpwstring[i];
  1255. if (wchar >= CHAR_WIDTH_BUFFER_LENGTH)
  1256. {
  1257. //
  1258. // We have a Unicode character that is not in our
  1259. // cache, get all the characters outside the cache
  1260. // before getting the text extent on this part of the
  1261. // string.
  1262. //
  1263. cchUStart = i;
  1264. while (wchar >= CHAR_WIDTH_BUFFER_LENGTH &&
  1265. wchar != VK_TAB && i < nCount)
  1266. {
  1267. wchar = lpwstring[++i];
  1268. }
  1269. GetTextExtentPointW(hdc, (LPWSTR)lpwstring + cchUStart,
  1270. i-cchUStart, &size);
  1271. textextent += size.cx;
  1272. if (wchar == VK_TAB || i >= nCount)
  1273. {
  1274. cch = i;
  1275. break;
  1276. }
  1277. //
  1278. // We have a char that is in the cache, fall through.
  1279. //
  1280. }
  1281. //
  1282. // The width of this character is in the cache buffer.
  1283. //
  1284. textextent += ped->charWidthBuffer[wchar];
  1285. }
  1286. }
  1287. }
  1288. nCount -= cch;
  1289. }
  1290. else
  1291. {
  1292. //
  1293. // Gotta call the driver to do our text extent.
  1294. //
  1295. if (ped->fAnsi)
  1296. {
  1297. cch = (INT)Edit_FindTabA(lpstring, nCount);
  1298. GetTextExtentPointA(hdc, lpstring, cch, &size);
  1299. }
  1300. else
  1301. {
  1302. cch = (INT)Edit_FindTabW((LPWSTR) lpstring, nCount);
  1303. GetTextExtentPointW(hdc, (LPWSTR)lpstring, cch, &size);
  1304. }
  1305. nCount -= cch;
  1306. //
  1307. // Subtruct Overhang for Italic fonts.
  1308. //
  1309. textextent = size.cx - ped->charOverhang;
  1310. }
  1311. //
  1312. // textextent is computed.
  1313. //
  1314. xStripStPos = xEnd;
  1315. xEnd += (int)textextent;
  1316. xStripEndPos = xEnd;
  1317. //
  1318. // We will consider the negative widths only if when we draw opaquely.
  1319. //
  1320. if (fFirstPass && fDraw)
  1321. {
  1322. xRightmostPoint = max(xStripEndPos + (int)wNegCwidth, xRightmostPoint);
  1323. //
  1324. // Check if this strip peeps beyond the clip region.
  1325. //
  1326. if (xRightmostPoint > xClipEndPos)
  1327. {
  1328. if (!NegCInfoForStrip->nCount)
  1329. {
  1330. NegCInfoForStrip->lpString = lpstring;
  1331. NegCInfoForStrip->ichString = ichString;
  1332. NegCInfoForStrip->nCount = nCount+cch;
  1333. NegCInfoForStrip->XStartPos = xStripStPos;
  1334. }
  1335. }
  1336. }
  1337. if (ped->fAnsi)
  1338. {
  1339. //
  1340. // Possibly Points to a tab character.
  1341. //
  1342. lpTab = lpstring + cch;
  1343. }
  1344. else
  1345. {
  1346. lpwTab = ((LPWSTR)lpstring) + cch ;
  1347. }
  1348. //
  1349. // we must consider all the consecutive tabs and calculate the
  1350. // the begining of next strip.
  1351. //
  1352. nConsecutiveTabs = 0;
  1353. while (nCount &&
  1354. (ped->fAnsi ? (*lpTab == VK_TAB) : (*lpwTab == VK_TAB)))
  1355. {
  1356. //
  1357. // Find the next tab position and update the x value.
  1358. //
  1359. xTabStartPos = xEnd;
  1360. if (pixeltabstop)
  1361. {
  1362. xEnd = (((xEnd-iTabOrigin)/pixeltabstop)*pixeltabstop) +
  1363. pixeltabstop + iTabOrigin;
  1364. }
  1365. else
  1366. {
  1367. for (i = 0; i < nTabPositions; i++)
  1368. {
  1369. if (xEnd < (lpintTabStopPositions[i] + iTabOrigin))
  1370. {
  1371. xEnd = (lpintTabStopPositions[i] + iTabOrigin);
  1372. break;
  1373. }
  1374. }
  1375. //
  1376. // Check if all the tabstops set are exhausted; Then start using
  1377. // default tab stop positions.
  1378. //
  1379. if (i == nTabPositions)
  1380. {
  1381. pixeltabstop = 8*cxCharWidth;
  1382. xEnd = ((xEnd - iTabOrigin)/pixeltabstop)*pixeltabstop +
  1383. pixeltabstop + iTabOrigin;
  1384. }
  1385. }
  1386. if (fFirstPass && fDraw)
  1387. {
  1388. xRightmostPoint = max(xEnd, xRightmostPoint);
  1389. //
  1390. // Check if this strip peeps beyond the clip region
  1391. //
  1392. if (xRightmostPoint > xClipEndPos)
  1393. {
  1394. if (!NegCInfoForStrip->nCount)
  1395. {
  1396. NegCInfoForStrip->ichString = ichString + cch + nConsecutiveTabs;
  1397. NegCInfoForStrip->nCount = nCount;
  1398. NegCInfoForStrip->lpString = (ped->fAnsi ?
  1399. lpTab : (LPSTR) lpwTab);
  1400. NegCInfoForStrip->XStartPos = xTabStartPos;
  1401. }
  1402. }
  1403. }
  1404. nConsecutiveTabs++;
  1405. nCount--;
  1406. ped->fAnsi ? lpTab++ : (LPSTR) (lpwTab++) ; // Move to the next character.
  1407. }
  1408. if (fDraw)
  1409. {
  1410. if (fFirstPass)
  1411. {
  1412. //
  1413. // Is anything remaining to be drawn in this strip?
  1414. //
  1415. if (!nCount)
  1416. {
  1417. //
  1418. // No! We are done.
  1419. //
  1420. rc.right = xEnd;
  1421. }
  1422. else
  1423. {
  1424. //
  1425. // "x" is the effective starting position of next strip.
  1426. //
  1427. iTabLength = xEnd - xStripEndPos;
  1428. //
  1429. // Check if there is a possibility of this tab length being too small
  1430. // compared to the negative A and C widths if any.
  1431. //
  1432. if ((wNegCwidth + (wNegAwidth = ped->wMaxNegA)) > (UINT)iTabLength)
  1433. {
  1434. //
  1435. // Unfortunately, there is a possiblity of an overlap.
  1436. // Let us find out the actual NegA for the next strip.
  1437. //
  1438. wNegAwidth = GetActualNegA(
  1439. hdc,
  1440. ped,
  1441. xEnd,
  1442. lpstring + (cch + nConsecutiveTabs)*ped->cbChar,
  1443. ichString + cch + nConsecutiveTabs,
  1444. nCount,
  1445. &NegAInfo);
  1446. }
  1447. //
  1448. // Check if they actually overlap
  1449. //
  1450. if ((wNegCwidth + wNegAwidth) <= (UINT)iTabLength)
  1451. {
  1452. //
  1453. // No overlap between the strips. This is the ideal situation.
  1454. //
  1455. rc.right = xEnd - wNegAwidth;
  1456. }
  1457. else
  1458. {
  1459. //
  1460. // Yes! They overlap.
  1461. //
  1462. rc.right = xEnd;
  1463. //
  1464. // See if negative C width is too large compared to tab length.
  1465. //
  1466. if (wNegCwidth > (UINT)iTabLength)
  1467. {
  1468. //
  1469. // Must redraw transparently a part of the current strip later.
  1470. //
  1471. if (RedrawStripInfo.nCount)
  1472. {
  1473. //
  1474. // A previous strip also needs to be redrawn; So, merge this
  1475. // strip to that strip.
  1476. //
  1477. RedrawStripInfo.nCount = (ichString -
  1478. RedrawStripInfo.ichString) + cch;
  1479. }
  1480. else
  1481. {
  1482. RedrawStripInfo.nCount = cch;
  1483. RedrawStripInfo.lpString = lpstring;
  1484. RedrawStripInfo.ichString = ichString;
  1485. RedrawStripInfo.XStartPos = xStripStPos;
  1486. }
  1487. }
  1488. if (wNegAwidth)
  1489. {
  1490. //
  1491. // Must redraw transparently the first part of the next strip later.
  1492. //
  1493. if (RedrawStripInfo.nCount)
  1494. {
  1495. //
  1496. // A previous strip also needs to be redrawn; So, merge this
  1497. // strip to that strip.
  1498. //
  1499. RedrawStripInfo.nCount = (NegAInfo.ichString - RedrawStripInfo.ichString) +
  1500. NegAInfo.nCount;
  1501. }
  1502. else
  1503. {
  1504. RedrawStripInfo = NegAInfo;
  1505. }
  1506. }
  1507. }
  1508. }
  1509. }
  1510. if (rc.left < xClipEndPos)
  1511. {
  1512. if (fFirstPass)
  1513. {
  1514. //
  1515. // If this is the end of the strip, then complete the rectangle.
  1516. //
  1517. if ((!nCount) && (xClipEndPos == MAXCLIPENDPOS))
  1518. {
  1519. rc.right = max(rc.right, xClipEndPos);
  1520. }
  1521. else
  1522. {
  1523. rc.right = min(rc.right, xClipEndPos);
  1524. }
  1525. }
  1526. //
  1527. // Draw the current strip.
  1528. //
  1529. if (rc.left < rc.right)
  1530. {
  1531. if (ped->fAnsi)
  1532. {
  1533. ExtTextOutA(hdc,
  1534. xStripStPos,
  1535. y,
  1536. (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
  1537. (LPRECT)&rc, lpstring, cch, 0L);
  1538. }
  1539. else
  1540. {
  1541. ExtTextOutW(hdc,
  1542. xStripStPos,
  1543. y,
  1544. (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
  1545. (LPRECT)&rc, (LPWSTR)lpstring, cch, 0L);
  1546. }
  1547. }
  1548. }
  1549. if (fFirstPass)
  1550. {
  1551. rc.left = max(rc.right, xClipStPos);
  1552. }
  1553. ichString += (cch+nConsecutiveTabs);
  1554. }
  1555. //
  1556. // Skip over the tab and the characters we just drew.
  1557. //
  1558. lpstring += (cch + nConsecutiveTabs) * ped->cbChar;
  1559. }
  1560. xEndOfStrip = xEnd;
  1561. //
  1562. // check if we need to draw some portions transparently.
  1563. //
  1564. if (fFirstPass && fDraw && RedrawStripInfo.nCount)
  1565. {
  1566. iSavedBkMode = SetBkMode(hdc, TRANSPARENT);
  1567. fFirstPass = FALSE;
  1568. nCount = RedrawStripInfo.nCount;
  1569. rc.left = xClipStPos;
  1570. rc.right = xClipEndPos;
  1571. lpstring = RedrawStripInfo.lpString;
  1572. ichString = RedrawStripInfo.ichString;
  1573. xEnd = RedrawStripInfo.XStartPos;
  1574. //
  1575. // Redraw Transparently.
  1576. //
  1577. goto RedrawStrip;
  1578. }
  1579. //
  1580. // Did we change the Bk mode?
  1581. //
  1582. if (iSavedBkMode)
  1583. {
  1584. SetBkMode(hdc, iSavedBkMode);
  1585. }
  1586. uRet = (UINT)(xEndOfStrip - xStart);
  1587. }
  1588. SetTextColor(hdc, clrTextSave);
  1589. SetBkColor(hdc, clrBkSave);
  1590. if (hbrBack && fNeedDelete)
  1591. {
  1592. DeleteObject(hbrBack);
  1593. }
  1594. return uRet;
  1595. }
  1596. //---------------------------------------------------------------------------//
  1597. //
  1598. // Edit_CchInWidth AorW
  1599. //
  1600. // Returns maximum count of characters (up to cch) from the given
  1601. // string (starting either at the beginning and moving forward or at the
  1602. // end and moving backwards based on the setting of the fForward flag)
  1603. // which will fit in the given width. ie. Will tell you how much of
  1604. // lpstring will fit in the given width even when using proportional
  1605. // characters. WARNING: If we use kerning, then this loses...
  1606. //
  1607. // NOTE: Edit_CchInWidth is not called if the language pack is loaded.
  1608. //
  1609. ICH Edit_CchInWidth(
  1610. PED ped,
  1611. HDC hdc,
  1612. LPSTR lpText,
  1613. ICH cch,
  1614. INT width,
  1615. BOOL fForward)
  1616. {
  1617. INT stringExtent;
  1618. INT cchhigh;
  1619. INT cchnew = 0;
  1620. INT cchlow = 0;
  1621. SIZE size;
  1622. LPSTR lpStart;
  1623. if ((width <= 0) || !cch)
  1624. {
  1625. return (0);
  1626. }
  1627. //
  1628. // Optimize nonproportional fonts for single line ec since they don't have
  1629. // tabs.
  1630. //
  1631. //
  1632. // Change optimize condition for fixed pitch font
  1633. //
  1634. if (ped->fNonPropFont && ped->fSingle && !ped->fDBCS)
  1635. {
  1636. return Edit_AdjustIch(ped, lpText, umin(width/ped->aveCharWidth, (INT)cch));
  1637. }
  1638. //
  1639. // Check if password hidden chars are being used.
  1640. //
  1641. if (ped->charPasswordChar)
  1642. {
  1643. return (umin(width / ped->cPasswordCharWidth, (INT)cch));
  1644. }
  1645. //
  1646. // ALWAYS RESTRICT TO AT MOST MAXLINELENGTH to avoid overflow...
  1647. //
  1648. cch = umin(MAXLINELENGTH, cch);
  1649. cchhigh = cch + 1;
  1650. while (cchlow < cchhigh - 1)
  1651. {
  1652. cchnew = umax((cchhigh - cchlow) / 2, 1) + cchlow;
  1653. lpStart = lpText;
  1654. //
  1655. // If we want to figure out how many fit starting at the end and moving
  1656. // backwards, make sure we move to the appropriate position in the
  1657. // string before calculating the text extent.
  1658. //
  1659. if (!fForward)
  1660. {
  1661. lpStart += (cch - cchnew)*ped->cbChar;
  1662. }
  1663. if (ped->fSingle)
  1664. {
  1665. if (ped->fAnsi)
  1666. {
  1667. GetTextExtentPointA(hdc, (LPSTR)lpStart, cchnew, &size);
  1668. }
  1669. else
  1670. {
  1671. GetTextExtentPointW(hdc, (LPWSTR)lpStart, cchnew, &size);
  1672. }
  1673. stringExtent = size.cx;
  1674. }
  1675. else
  1676. {
  1677. stringExtent = Edit_TabTheTextOut(hdc, 0, 0, 0, 0,
  1678. lpStart,
  1679. cchnew, 0,
  1680. ped, 0, ECT_CALC, NULL );
  1681. }
  1682. if (stringExtent > width)
  1683. {
  1684. cchhigh = cchnew;
  1685. }
  1686. else
  1687. {
  1688. cchlow = cchnew;
  1689. }
  1690. }
  1691. //
  1692. // Call Edit_AdjustIch ( generic case )
  1693. //
  1694. cchlow = Edit_AdjustIch(ped, lpText, cchlow);
  1695. return cchlow;
  1696. }
  1697. //---------------------------------------------------------------------------//
  1698. //
  1699. // Edit_FindTab
  1700. //
  1701. // Scans lpstr and return s the number of CHARs till the first TAB.
  1702. // Scans at most cch chars of lpstr.
  1703. //
  1704. ICH Edit_FindTabA(
  1705. LPSTR lpstr,
  1706. ICH cch)
  1707. {
  1708. LPSTR copylpstr = lpstr;
  1709. if (cch)
  1710. {
  1711. while (*lpstr != VK_TAB)
  1712. {
  1713. lpstr++;
  1714. if (--cch == 0)
  1715. {
  1716. break;
  1717. }
  1718. }
  1719. }
  1720. return (ICH)(lpstr - copylpstr);
  1721. }
  1722. //---------------------------------------------------------------------------//
  1723. //
  1724. ICH Edit_FindTabW(
  1725. LPWSTR lpstr,
  1726. ICH cch)
  1727. {
  1728. LPWSTR copylpstr = lpstr;
  1729. if (cch)
  1730. {
  1731. while (*lpstr != VK_TAB)
  1732. {
  1733. lpstr++;
  1734. if (--cch == 0)
  1735. {
  1736. break;
  1737. }
  1738. }
  1739. }
  1740. return ((ICH)(lpstr - copylpstr));
  1741. }
  1742. //---------------------------------------------------------------------------//
  1743. //
  1744. // Edit_GetBrush()
  1745. //
  1746. // Gets appropriate background brush to erase with.
  1747. //
  1748. HBRUSH Edit_GetBrush(PED ped, HDC hdc, LPBOOL pfNeedDelete)
  1749. {
  1750. HBRUSH hbr;
  1751. COLORREF clr;
  1752. HRESULT hr = E_FAIL;
  1753. #ifdef _USE_DRAW_THEME_TEXT_
  1754. if (ped->hTheme)
  1755. {
  1756. INT iStateId = Edit_GetStateId(ped);
  1757. hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iStateId, TMT_FILLCOLOR, &clr);
  1758. if (SUCCEEDED(hr))
  1759. {
  1760. hbr = CreateSolidBrush(clr);
  1761. if (pfNeedDelete)
  1762. {
  1763. //
  1764. // tell the caller this brush needs to be deleted
  1765. //
  1766. *pfNeedDelete = TRUE;
  1767. }
  1768. }
  1769. }
  1770. #endif // _USE_DRAW_THEME_TEXT_
  1771. if (!ped->hTheme || FAILED(hr))
  1772. {
  1773. BOOL f40Compat;
  1774. f40Compat = Is400Compat(UserGetVersion());
  1775. //
  1776. // Get background brush
  1777. //
  1778. if ((ped->fReadOnly || ped->fDisabled) && f40Compat)
  1779. {
  1780. hbr = Edit_GetControlBrush(ped, hdc, WM_CTLCOLORSTATIC);
  1781. }
  1782. else
  1783. {
  1784. hbr = Edit_GetControlBrush(ped, hdc, WM_CTLCOLOREDIT);
  1785. }
  1786. if (ped->fDisabled && (ped->fSingle || f40Compat))
  1787. {
  1788. //
  1789. // Change text color
  1790. //
  1791. clr = GetSysColor(COLOR_GRAYTEXT);
  1792. if (clr != GetBkColor(hdc))
  1793. {
  1794. SetTextColor(hdc, clr);
  1795. }
  1796. }
  1797. }
  1798. return hbr;
  1799. }
  1800. //---------------------------------------------------------------------------//
  1801. //
  1802. // NextWordCallBack
  1803. //
  1804. VOID NextWordCallBack(PED ped, ICH ichStart, BOOL fLeft, ICH *pichMin, ICH *pichMax)
  1805. {
  1806. ICH ichMinSel;
  1807. ICH ichMaxSel;
  1808. LPSTR pText;
  1809. pText = Edit_Lock(ped);
  1810. if (fLeft ||
  1811. (!(BOOL)CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_ISDELIMITER) &&
  1812. (ped->fAnsi ? (*(pText + ichStart) != VK_RETURN) : (*((LPWSTR)pText + ichStart) != VK_RETURN))))
  1813. {
  1814. ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_LEFT);
  1815. }
  1816. else
  1817. {
  1818. ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_RIGHT);
  1819. }
  1820. ichMaxSel = min(ichMinSel + 1, ped->cch);
  1821. if (ped->fAnsi)
  1822. {
  1823. if (*(pText + ichMinSel) == VK_RETURN)
  1824. {
  1825. if (ichMinSel > 0 && *(pText + ichMinSel - 1) == VK_RETURN)
  1826. {
  1827. //
  1828. // So that we can treat CRCRLF as one word also.
  1829. //
  1830. ichMinSel--;
  1831. }
  1832. else if (*(pText+ichMinSel + 1) == VK_RETURN)
  1833. {
  1834. //
  1835. // Move MaxSel on to the LF
  1836. //
  1837. ichMaxSel++;
  1838. }
  1839. }
  1840. }
  1841. else
  1842. {
  1843. if (*((LPWSTR)pText + ichMinSel) == VK_RETURN)
  1844. {
  1845. if (ichMinSel > 0 && *((LPWSTR)pText + ichMinSel - 1) == VK_RETURN)
  1846. {
  1847. //
  1848. // So that we can treat CRCRLF as one word also.
  1849. //
  1850. ichMinSel--;
  1851. }
  1852. else if (*((LPWSTR)pText+ichMinSel + 1) == VK_RETURN)
  1853. {
  1854. //
  1855. // Move MaxSel on to the LF
  1856. //
  1857. ichMaxSel++;
  1858. }
  1859. }
  1860. }
  1861. ichMaxSel = CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichMaxSel, ped->cch, WB_RIGHT);
  1862. Edit_Unlock(ped);
  1863. if (pichMin)
  1864. {
  1865. *pichMin = ichMinSel;
  1866. }
  1867. if (pichMax)
  1868. {
  1869. *pichMax = ichMaxSel;
  1870. }
  1871. }
  1872. //---------------------------------------------------------------------------//
  1873. //
  1874. // NextWordLpkCallback
  1875. //
  1876. // Identifies next/prev word position for complex scripts
  1877. //
  1878. VOID NextWordLpkCallBack(PED ped, ICH ichStart, BOOL fLeft, ICH *pichMin, ICH *pichMax)
  1879. {
  1880. PSTR pText = Edit_Lock(ped);
  1881. HDC hdc = Edit_GetDC(ped, TRUE);
  1882. ped->pLpkEditCallout->EditNextWord((PED0)ped, hdc, pText, ichStart, fLeft, pichMin, pichMax);
  1883. Edit_ReleaseDC(ped, hdc, TRUE);
  1884. Edit_Unlock(ped);
  1885. }
  1886. //---------------------------------------------------------------------------//
  1887. //
  1888. // Edit_Word
  1889. //
  1890. // if fLeft, Returns the ichMinSel and ichMaxSel of the word to the
  1891. // left of ichStart. ichMinSel contains the starting letter of the word,
  1892. // ichmaxsel contains all spaces up to the first character of the next word.
  1893. //
  1894. // if !fLeft, Returns the ichMinSel and ichMaxSel of the word to the right of
  1895. // ichStart. ichMinSel contains the starting letter of the word, ichmaxsel
  1896. // contains the first letter of the next word. If ichStart is in the middle
  1897. // of a word, that word is considered the left or right word.
  1898. //
  1899. // A CR LF pair or CRCRLF triple is considered a single word in
  1900. // multiline edit controls.
  1901. //
  1902. VOID Edit_Word(PED ped, ICH ichStart, BOOL fLeft, LPICH pichMin, LPICH pichMax)
  1903. {
  1904. BOOL charLocated = FALSE;
  1905. BOOL spaceLocated = FALSE;
  1906. if ((!ichStart && fLeft) || (ichStart == ped->cch && !fLeft))
  1907. {
  1908. //
  1909. // We are at the beginning of the text (looking left) or we are at end
  1910. // of text (looking right), no word here
  1911. //
  1912. if (pichMin)
  1913. {
  1914. *pichMin = 0;
  1915. }
  1916. if (pichMax)
  1917. {
  1918. *pichMax = 0;
  1919. }
  1920. return;
  1921. }
  1922. //
  1923. // Don't give out hints about word breaks if password chars are being used,
  1924. //
  1925. if (ped->charPasswordChar)
  1926. {
  1927. if (pichMin)
  1928. {
  1929. *pichMin = 0;
  1930. }
  1931. if (pichMax)
  1932. {
  1933. *pichMax = ped->cch;
  1934. }
  1935. return;
  1936. }
  1937. if (ped->fAnsi)
  1938. {
  1939. PSTR pText;
  1940. PSTR pWordMinSel;
  1941. PSTR pWordMaxSel;
  1942. PSTR pPrevChar;
  1943. UserAssert(ped->cbChar == sizeof(CHAR));
  1944. if (ped->lpfnNextWord)
  1945. {
  1946. NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1947. return;
  1948. }
  1949. if (ped->pLpkEditCallout)
  1950. {
  1951. NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  1952. return;
  1953. }
  1954. pText = Edit_Lock(ped);
  1955. pWordMinSel = pWordMaxSel = pText + ichStart;
  1956. //
  1957. // if fLeft: Move pWordMinSel to the left looking for the start of a word.
  1958. // If we start at a space, we will include spaces in the selection as we
  1959. // move left untill we find a nonspace character. At that point, we continue
  1960. // looking left until we find a space. Thus, the selection will consist of
  1961. // a word with its trailing spaces or, it will consist of any leading at the
  1962. // beginning of a line of text.
  1963. //
  1964. //
  1965. // if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
  1966. // word. If the pWordMinSel points to a character, then we move left
  1967. // looking for a space which will signify the start of the word. If
  1968. // pWordMinSel points to a space, we look right till we come upon a
  1969. // character. pMaxWord will look right starting at pMinWord looking for the
  1970. // end of the word and its trailing spaces.
  1971. //
  1972. if (fLeft || !ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0D)
  1973. {
  1974. //
  1975. // If we are moving left or if we are moving right and we are not on a
  1976. // space or a CR (the start of a word), then we was look left for the
  1977. // start of a word which is either a CR or a character. We do this by
  1978. // looking left till we find a character (or if CR we stop), then we
  1979. // continue looking left till we find a space or LF.
  1980. //
  1981. while (pWordMinSel > pText && ((!ISDELIMETERA(*(pWordMinSel - 1)) &&
  1982. *(pWordMinSel - 1) != 0x0A) || !charLocated))
  1983. {
  1984. //
  1985. // Treat double byte character as a word ( in ansi pWordMinSel loop )
  1986. //
  1987. pPrevChar = Edit_AnsiPrev( ped, pText, pWordMinSel );
  1988. //
  1989. // are looking right ( !fLeft ).
  1990. // current character is a double byte chararacter or
  1991. // character is a double byte character, we
  1992. // on the beggining of a word.
  1993. //
  1994. if ( !fLeft && ( ISDELIMETERA( *pPrevChar ) ||
  1995. *pPrevChar == 0x0A ||
  1996. Edit_IsDBCSLeadByte(ped, *pWordMinSel) ||
  1997. pWordMinSel - pPrevChar == 2 ) )
  1998. {
  1999. //
  2000. // If we are looking for the start of the word right, then we
  2001. // stop when we have found it. (needed in case charLocated is
  2002. // still FALSE)
  2003. //
  2004. break;
  2005. }
  2006. if (pWordMinSel - pPrevChar == 2)
  2007. {
  2008. //
  2009. // character is a double byte character.
  2010. // we are in a word ( charLocated == TRUE )
  2011. // position is the beginning of the word
  2012. // we are not in a word ( charLocated == FALSE )
  2013. // previous character is what we looking for.
  2014. //
  2015. if (!charLocated)
  2016. {
  2017. pWordMinSel = pPrevChar;
  2018. }
  2019. break;
  2020. }
  2021. pWordMinSel = pPrevChar;
  2022. if (!ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0A)
  2023. {
  2024. //
  2025. // We have found the last char in the word. Continue looking
  2026. // backwards till we find the first char of the word
  2027. //
  2028. charLocated = TRUE;
  2029. //
  2030. // We will consider a CR the start of a word
  2031. //
  2032. if (*pWordMinSel == 0x0D)
  2033. {
  2034. break;
  2035. }
  2036. }
  2037. }
  2038. }
  2039. else
  2040. {
  2041. while ((ISDELIMETERA(*pWordMinSel) || *pWordMinSel == 0x0A) && pWordMinSel < pText + ped->cch)
  2042. {
  2043. pWordMinSel++;
  2044. }
  2045. }
  2046. //
  2047. // Adjust the initial position of pWordMaxSel ( in ansi )
  2048. //
  2049. pWordMaxSel = Edit_AnsiNext(ped, pWordMinSel);
  2050. pWordMaxSel = min(pWordMaxSel, pText + ped->cch);
  2051. //
  2052. // pWordMinSel points a double byte character AND
  2053. // points non space
  2054. // then
  2055. // pWordMaxSel points the beggining of next word.
  2056. //
  2057. if ((pWordMaxSel - pWordMinSel == 2) && !ISDELIMETERA(*pWordMaxSel))
  2058. {
  2059. goto FastReturnA;
  2060. }
  2061. if (*pWordMinSel == 0x0D)
  2062. {
  2063. if (pWordMinSel > pText && *(pWordMinSel - 1) == 0x0D)
  2064. {
  2065. //
  2066. // So that we can treat CRCRLF as one word also.
  2067. //
  2068. pWordMinSel--;
  2069. }
  2070. else if (*(pWordMinSel + 1) == 0x0D)
  2071. {
  2072. //
  2073. // Move MaxSel on to the LF
  2074. //
  2075. pWordMaxSel++;
  2076. }
  2077. }
  2078. //
  2079. // Check if we have a one character word
  2080. //
  2081. if (ISDELIMETERA(*pWordMaxSel))
  2082. {
  2083. spaceLocated = TRUE;
  2084. }
  2085. //
  2086. // Move pWordMaxSel to the right looking for the end of a word and its
  2087. // trailing spaces. WordMaxSel stops on the first character of the next
  2088. // word. Thus, we break either at a CR or at the first nonspace char after
  2089. // a run of spaces or LFs.
  2090. //
  2091. while ((pWordMaxSel < pText + ped->cch) && (!spaceLocated || (ISDELIMETERA(*pWordMaxSel))))
  2092. {
  2093. if (*pWordMaxSel == 0x0D)
  2094. {
  2095. break;
  2096. }
  2097. //
  2098. // Treat double byte character as a word ( in ansi pWordMaxSel loop )
  2099. // if it's a double byte character then
  2100. // we are at the beginning of next word
  2101. // which is a double byte character.
  2102. //
  2103. if (Edit_IsDBCSLeadByte( ped, *pWordMaxSel))
  2104. {
  2105. break;
  2106. }
  2107. pWordMaxSel++;
  2108. if (ISDELIMETERA(*pWordMaxSel))
  2109. {
  2110. spaceLocated = TRUE;
  2111. }
  2112. if (*(pWordMaxSel - 1) == 0x0A)
  2113. {
  2114. break;
  2115. }
  2116. }
  2117. //
  2118. // label for fast return ( for Ansi )
  2119. //
  2120. FastReturnA:
  2121. Edit_Unlock(ped);
  2122. if (pichMin)
  2123. {
  2124. *pichMin = (ICH)(pWordMinSel - pText);
  2125. }
  2126. if (pichMax)
  2127. {
  2128. *pichMax = (ICH)(pWordMaxSel - pText);
  2129. }
  2130. }
  2131. else
  2132. {
  2133. LPWSTR pwText;
  2134. LPWSTR pwWordMinSel;
  2135. LPWSTR pwWordMaxSel;
  2136. BOOL charLocated = FALSE;
  2137. BOOL spaceLocated = FALSE;
  2138. PWSTR pwPrevChar;
  2139. UserAssert(ped->cbChar == sizeof(WCHAR));
  2140. if (ped->lpfnNextWord)
  2141. {
  2142. NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  2143. return;
  2144. }
  2145. if (ped->pLpkEditCallout)
  2146. {
  2147. NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
  2148. return;
  2149. }
  2150. pwText = (LPWSTR)Edit_Lock(ped);
  2151. pwWordMinSel = pwWordMaxSel = pwText + ichStart;
  2152. //
  2153. // if fLeft: Move pWordMinSel to the left looking for the start of a word.
  2154. // If we start at a space, we will include spaces in the selection as we
  2155. // move left untill we find a nonspace character. At that point, we continue
  2156. // looking left until we find a space. Thus, the selection will consist of
  2157. // a word with its trailing spaces or, it will consist of any leading at the
  2158. // beginning of a line of text.
  2159. //
  2160. //
  2161. // if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
  2162. // word. If the pWordMinSel points to a character, then we move left
  2163. // looking for a space which will signify the start of the word. If
  2164. // pWordMinSel points to a space, we look right till we come upon a
  2165. // character. pMaxWord will look right starting at pMinWord looking for the
  2166. // end of the word and its trailing spaces.
  2167. //
  2168. if (fLeft || (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0D))
  2169. {
  2170. //
  2171. // If we are moving left or if we are moving right and we are not on a
  2172. // space or a CR (the start of a word), then we was look left for the
  2173. // start of a word which is either a CR or a character. We do this by
  2174. // looking left till we find a character (or if CR we stop), then we
  2175. //
  2176. // continue looking left till we find a space or LF.
  2177. while (pwWordMinSel > pwText && ((!ISDELIMETERW(*(pwWordMinSel - 1)) && *(pwWordMinSel - 1) != 0x0A) || !charLocated))
  2178. {
  2179. //
  2180. // Treat double byte character as a word ( in unicode pwWordMinSel loop )
  2181. //
  2182. pwPrevChar = pwWordMinSel - 1;
  2183. //
  2184. // we are looking right ( !fLeft ).
  2185. //
  2186. // if current character is a double width chararacter
  2187. // or previous character is a double width character,
  2188. // we are on the beggining of a word.
  2189. //
  2190. if (!fLeft && (ISDELIMETERW( *pwPrevChar) ||
  2191. *pwPrevChar == 0x0A ||
  2192. Edit_IsFullWidth(CP_ACP,*pwWordMinSel) ||
  2193. Edit_IsFullWidth(CP_ACP,*pwPrevChar)))
  2194. {
  2195. //
  2196. // If we are looking for the start of the word right, then we
  2197. // stop when we have found it. (needed in case charLocated is
  2198. // still FALSE)
  2199. //
  2200. break;
  2201. }
  2202. if (Edit_IsFullWidth(CP_ACP,*pwPrevChar))
  2203. {
  2204. //
  2205. // Previous character is a double width character.
  2206. //
  2207. // if we are in a word ( charLocated == TRUE )
  2208. // current position is the beginning of the word
  2209. // if we are not in a word ( charLocated == FALSE )
  2210. // the previous character is what we looking for.
  2211. //
  2212. if ( !charLocated )
  2213. {
  2214. pwWordMinSel = pwPrevChar;
  2215. }
  2216. break;
  2217. }
  2218. pwWordMinSel = pwPrevChar;
  2219. if (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0A)
  2220. {
  2221. //
  2222. // We have found the last char in the word. Continue looking
  2223. // backwards till we find the first char of the word
  2224. //
  2225. charLocated = TRUE;
  2226. //
  2227. // We will consider a CR the start of a word
  2228. //
  2229. if (*pwWordMinSel == 0x0D)
  2230. {
  2231. break;
  2232. }
  2233. }
  2234. }
  2235. }
  2236. else
  2237. {
  2238. //
  2239. // We are moving right and we are in between words so we need to move
  2240. // right till we find the start of a word (either a CR or a character.
  2241. //
  2242. while ((ISDELIMETERW(*pwWordMinSel) || *pwWordMinSel == 0x0A) && pwWordMinSel < pwText + ped->cch)
  2243. {
  2244. pwWordMinSel++;
  2245. }
  2246. }
  2247. pwWordMaxSel = min((pwWordMinSel + 1), (pwText + ped->cch));
  2248. //
  2249. // If pwWordMinSel points a double width character AND
  2250. // pwWordMaxSel points non space then
  2251. // pwWordMaxSel points the beggining of next word.
  2252. //
  2253. if (Edit_IsFullWidth(CP_ACP,*pwWordMinSel) && ! ISDELIMETERW(*pwWordMaxSel))
  2254. {
  2255. goto FastReturnW;
  2256. }
  2257. if (*pwWordMinSel == 0x0D)
  2258. {
  2259. if (pwWordMinSel > pwText && *(pwWordMinSel - 1) == 0x0D)
  2260. {
  2261. //
  2262. // So that we can treat CRCRLF as one word also.
  2263. //
  2264. pwWordMinSel--;
  2265. }
  2266. else if (*(pwWordMinSel + 1) == 0x0D)
  2267. {
  2268. //
  2269. // Move MaxSel on to the LF
  2270. //
  2271. pwWordMaxSel++;
  2272. }
  2273. }
  2274. //
  2275. // Check if we have a one character word
  2276. //
  2277. if (ISDELIMETERW(*pwWordMaxSel))
  2278. {
  2279. spaceLocated = TRUE;
  2280. }
  2281. //
  2282. // Move pwWordMaxSel to the right looking for the end of a word and its
  2283. // trailing spaces. WordMaxSel stops on the first character of the next
  2284. // word. Thus, we break either at a CR or at the first nonspace char after
  2285. // a run of spaces or LFs.
  2286. //
  2287. while ((pwWordMaxSel < pwText + ped->cch) && (!spaceLocated || (ISDELIMETERW(*pwWordMaxSel))))
  2288. {
  2289. if (*pwWordMaxSel == 0x0D)
  2290. {
  2291. break;
  2292. }
  2293. //
  2294. // treat double byte character as a word ( in unicode pwWordMaxSel loop )
  2295. // if it's a double width character
  2296. // then we are at the beginning of
  2297. // the next word which is a double
  2298. // width character.
  2299. //
  2300. if (Edit_IsFullWidth(CP_ACP,*pwWordMaxSel))
  2301. {
  2302. break;
  2303. }
  2304. pwWordMaxSel++;
  2305. if (ISDELIMETERW(*pwWordMaxSel))
  2306. {
  2307. spaceLocated = TRUE;
  2308. }
  2309. if (*(pwWordMaxSel - 1) == 0x0A)
  2310. {
  2311. break;
  2312. }
  2313. }
  2314. //
  2315. // label for fast return ( for Unicode )
  2316. //
  2317. FastReturnW:
  2318. Edit_Unlock(ped);
  2319. if (pichMin)
  2320. {
  2321. *pichMin = (ICH)(pwWordMinSel - pwText);
  2322. }
  2323. if (pichMax)
  2324. {
  2325. *pichMax = (ICH)(pwWordMaxSel - pwText);
  2326. }
  2327. }
  2328. }
  2329. //---------------------------------------------------------------------------//
  2330. //
  2331. // Edit_SaveUndo()
  2332. //
  2333. // Saves old undo information into given buffer, and clears out info in
  2334. // passed in undo buffer. If we're restoring, pundoFrom and pundoTo are
  2335. // reversed.
  2336. //
  2337. VOID Edit_SaveUndo(PUNDO pundoFrom, PUNDO pundoTo, BOOL fClear)
  2338. {
  2339. //
  2340. // Save undo data
  2341. //
  2342. RtlCopyMemory(pundoTo, pundoFrom, sizeof(UNDO));
  2343. //
  2344. // Clear passed in undo buffer
  2345. //
  2346. if (fClear)
  2347. {
  2348. RtlZeroMemory(pundoFrom, sizeof(UNDO));
  2349. }
  2350. }
  2351. //---------------------------------------------------------------------------//
  2352. //
  2353. // Edit_EmptyUndo AorW
  2354. //
  2355. // Empties the undo buffer.
  2356. //
  2357. VOID Edit_EmptyUndo(PUNDO pundo)
  2358. {
  2359. if (pundo->hDeletedText)
  2360. {
  2361. GlobalFree(pundo->hDeletedText);
  2362. }
  2363. RtlZeroMemory(pundo, sizeof(UNDO));
  2364. }
  2365. //---------------------------------------------------------------------------//
  2366. //
  2367. // Edit_MergeUndoInsertInfo() -
  2368. //
  2369. // When an insert takes place, this function is called with the info about
  2370. // the new insertion (the insertion point and the count of chars inserted);
  2371. // This looks at the existing Undo info and merges the new new insert info
  2372. // with it.
  2373. //
  2374. VOID Edit_MergeUndoInsertInfo(PUNDO pundo, ICH ichInsert, ICH cchInsert)
  2375. {
  2376. //
  2377. // If undo buffer is empty, just insert the new info as UNDO_INSERT
  2378. //
  2379. if (pundo->undoType == UNDO_NONE)
  2380. {
  2381. pundo->undoType = UNDO_INSERT;
  2382. pundo->ichInsStart = ichInsert;
  2383. pundo->ichInsEnd = ichInsert+cchInsert;
  2384. }
  2385. else if (pundo->undoType & UNDO_INSERT)
  2386. {
  2387. //
  2388. // If there's already some undo insert info,
  2389. // try to merge the two.
  2390. //
  2391. //
  2392. // Check they are adjacent.
  2393. //
  2394. if (pundo->ichInsEnd == ichInsert)
  2395. {
  2396. //
  2397. // if so, just concatenate.
  2398. //
  2399. pundo->ichInsEnd += cchInsert;
  2400. }
  2401. else
  2402. {
  2403. //
  2404. // The new insert is not contiguous with the old one.
  2405. //
  2406. UNDOINSERT:
  2407. //
  2408. // If there is some UNDO_DELETE info already here, check to see
  2409. // if the new insert takes place at a point different from where
  2410. // that deletion occurred.
  2411. //
  2412. if ((pundo->undoType & UNDO_DELETE) && (pundo->ichDeleted != ichInsert))
  2413. {
  2414. //
  2415. // User is inserting into a different point; So, let us
  2416. // forget any UNDO_DELETE info;
  2417. //
  2418. if (pundo->hDeletedText)
  2419. {
  2420. GlobalFree(pundo->hDeletedText);
  2421. }
  2422. pundo->hDeletedText = NULL;
  2423. pundo->ichDeleted = 0xFFFFFFFF;
  2424. pundo->undoType &= ~UNDO_DELETE;
  2425. }
  2426. //
  2427. // Since the old insert and new insert are not adjacent, let us
  2428. // forget everything about the old insert and keep just the new
  2429. // insert info as the UNDO_INSERT.
  2430. //
  2431. pundo->ichInsStart = ichInsert;
  2432. pundo->ichInsEnd = ichInsert + cchInsert;
  2433. pundo->undoType |= UNDO_INSERT;
  2434. }
  2435. }
  2436. else if (pundo->undoType == UNDO_DELETE)
  2437. {
  2438. //
  2439. // If there is some Delete Info already present go and handle it.
  2440. //
  2441. goto UNDOINSERT;
  2442. }
  2443. }
  2444. //---------------------------------------------------------------------------//
  2445. //
  2446. // Edit_InsertText AorW
  2447. //
  2448. // Adds cch characters from lpText into the ped->hText starting at
  2449. // ped->ichCaret. Returns TRUE if successful else FALSE. Updates
  2450. // ped->cchAlloc and ped->cch properly if additional memory was allocated or
  2451. // if characters were actually added. Updates ped->ichCaret to be at the end
  2452. // of the inserted text. min and maxsel are equal to ichcaret.
  2453. //
  2454. BOOL Edit_InsertText(PED ped, LPSTR lpText, ICH* pcchInsert)
  2455. {
  2456. PSTR pedText;
  2457. PSTR pTextBuff;
  2458. LONG style;
  2459. HANDLE hTextCopy;
  2460. DWORD allocamt;
  2461. //
  2462. // If the last byte (lpText[cchInsert - 1]) is a DBCS leading byte
  2463. // we need to adjust it.
  2464. //
  2465. *pcchInsert = Edit_AdjustIch(ped, lpText, *pcchInsert);
  2466. if (!*pcchInsert)
  2467. {
  2468. return TRUE;
  2469. }
  2470. //
  2471. // Do we already have enough memory??
  2472. //
  2473. if (*pcchInsert >= (ped->cchAlloc - ped->cch))
  2474. {
  2475. //
  2476. // Allocate what we need plus a little extra. Return FALSE if we are
  2477. // unsuccessful.
  2478. //
  2479. allocamt = (ped->cch + *pcchInsert) * ped->cbChar;
  2480. allocamt += CCHALLOCEXTRA;
  2481. // if (!ped->fSingle)
  2482. // {
  2483. hTextCopy = LocalReAlloc(ped->hText, allocamt, LHND);
  2484. if (hTextCopy)
  2485. {
  2486. ped->hText = hTextCopy;
  2487. }
  2488. else
  2489. {
  2490. return FALSE;
  2491. }
  2492. // }
  2493. // else
  2494. // {
  2495. // if (!LocalReallocSafe(ped->hText, allocamt, LHND, pped))
  2496. // return FALSE;
  2497. // }
  2498. ped->cchAlloc = (ICH) LocalSize(ped->hText) / ped->cbChar;
  2499. }
  2500. //
  2501. // Ok, we got the memory. Now copy the text into the structure
  2502. //
  2503. pedText = Edit_Lock(ped);
  2504. if (ped->pLpkEditCallout)
  2505. {
  2506. HDC hdc;
  2507. INT iResult;
  2508. hdc = Edit_GetDC(ped, TRUE);
  2509. iResult = ped->pLpkEditCallout->EditVerifyText((PED0)ped, hdc, pedText, ped->ichCaret, lpText, *pcchInsert);
  2510. Edit_ReleaseDC (ped, hdc, TRUE);
  2511. if (iResult == 0)
  2512. {
  2513. Edit_Unlock (ped);
  2514. return TRUE;
  2515. }
  2516. }
  2517. //
  2518. // Get a pointer to the place where text is to be inserted
  2519. //
  2520. pTextBuff = pedText + ped->ichCaret * ped->cbChar;
  2521. if (ped->ichCaret != ped->cch)
  2522. {
  2523. //
  2524. // We are inserting text into the middle. We have to shift text to the
  2525. // right before inserting new text.
  2526. //
  2527. memmove(pTextBuff + *pcchInsert * ped->cbChar, pTextBuff, (ped->cch-ped->ichCaret) * ped->cbChar);
  2528. }
  2529. //
  2530. // Make a copy of the text being inserted in the edit buffer.
  2531. // Use this copy for doing UPPERCASE/LOWERCASE ANSI/OEM conversions
  2532. // Fix for Bug #3406 -- 01/29/91 -- SANKAR --
  2533. //
  2534. memmove(pTextBuff, lpText, *pcchInsert * ped->cbChar);
  2535. ped->cch += *pcchInsert;
  2536. //
  2537. // Get the control's style
  2538. //
  2539. style = GET_STYLE(ped);
  2540. //
  2541. // Do the Upper/Lower conversion
  2542. //
  2543. if (style & ES_LOWERCASE)
  2544. {
  2545. if (ped->fAnsi)
  2546. {
  2547. CharLowerBuffA((LPSTR)pTextBuff, *pcchInsert);
  2548. }
  2549. else
  2550. {
  2551. CharLowerBuffW((LPWSTR)pTextBuff, *pcchInsert);
  2552. }
  2553. }
  2554. else
  2555. {
  2556. if (style & ES_UPPERCASE)
  2557. {
  2558. if (ped->fAnsi)
  2559. {
  2560. CharUpperBuffA(pTextBuff, *pcchInsert);
  2561. }
  2562. else
  2563. {
  2564. CharUpperBuffW((LPWSTR)pTextBuff, *pcchInsert);
  2565. }
  2566. }
  2567. }
  2568. //
  2569. // Do the OEM conversion
  2570. //
  2571. // For backward compatibility with NT4, we don't perform OEM conversion
  2572. // for older apps if the system locale is FarEast.
  2573. //
  2574. if ((style & ES_OEMCONVERT) &&
  2575. (!g_fDBCSEnabled || Is500Compat(UserGetVersion()) || GetOEMCP() != GetACP()))
  2576. {
  2577. ICH i;
  2578. if (ped->fAnsi)
  2579. {
  2580. for (i = 0; i < *pcchInsert; i++)
  2581. {
  2582. //
  2583. // We don't need to call CharToOemBuff etc. if the character
  2584. // is a double byte character. And, calling Edit_IsDBCSLeadByte is
  2585. // faster and less complicated because we don't have to deal
  2586. // with the 2 byte dbcs cases.
  2587. //
  2588. if (g_fDBCSEnabled && Edit_IsDBCSLeadByte(ped, *(lpText+i)))
  2589. {
  2590. i++;
  2591. continue;
  2592. }
  2593. if (IsCharLowerA(*(pTextBuff + i)))
  2594. {
  2595. CharUpperBuffA(pTextBuff + i, 1);
  2596. CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
  2597. OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
  2598. CharLowerBuffA(pTextBuff + i, 1);
  2599. }
  2600. else
  2601. {
  2602. CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
  2603. OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
  2604. }
  2605. }
  2606. }
  2607. else
  2608. {
  2609. //
  2610. // Because 'ch' may become DBCS, and have a space for NULL.
  2611. //
  2612. UCHAR ch[4];
  2613. LPWSTR lpTextW = (LPWSTR)pTextBuff;
  2614. for (i = 0; i < *pcchInsert; i++)
  2615. {
  2616. if (*(lpTextW + i) == UNICODE_CARRIAGERETURN ||
  2617. *(lpTextW + i) == UNICODE_LINEFEED ||
  2618. *(lpTextW + i) == UNICODE_TAB)
  2619. {
  2620. continue;
  2621. }
  2622. if (IsCharLowerW(*(lpTextW + i)))
  2623. {
  2624. CharUpperBuffW(lpTextW + i, 1);
  2625. //
  2626. // make sure the null-terminate.
  2627. //
  2628. *(LPDWORD)ch = 0;
  2629. CharToOemBuffW(lpTextW + i, ch, 1);
  2630. //
  2631. // We assume any SBCS/DBCS character will converted
  2632. // to 1 Unicode char, Otherwise, we may overwrite
  2633. // next character...
  2634. //
  2635. OemToCharBuffW(ch, lpTextW + i, strlen(ch));
  2636. CharLowerBuffW(lpTextW + i, 1);
  2637. }
  2638. else
  2639. {
  2640. //
  2641. // make sure the null-terminate.
  2642. //
  2643. *(LPDWORD)ch = 0;
  2644. CharToOemBuffW(lpTextW + i, ch, 1);
  2645. //
  2646. // We assume any SBCS/DBCS character will converted
  2647. // to 1 Unicode char, Otherwise, we may overwrite
  2648. // next character...
  2649. //
  2650. OemToCharBuffW(ch, lpTextW + i, strlen(ch));
  2651. }
  2652. }
  2653. }
  2654. }
  2655. //
  2656. // Adjust UNDO fields so that we can undo this insert...
  2657. //
  2658. Edit_MergeUndoInsertInfo(Pundo(ped), ped->ichCaret, *pcchInsert);
  2659. ped->ichCaret += *pcchInsert;
  2660. if (ped->pLpkEditCallout)
  2661. {
  2662. HDC hdc;
  2663. hdc = Edit_GetDC(ped, TRUE);
  2664. ped->ichCaret = ped->pLpkEditCallout->EditAdjustCaret((PED0)ped, hdc, pedText, ped->ichCaret);
  2665. Edit_ReleaseDC (ped, hdc, TRUE);
  2666. }
  2667. ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;
  2668. Edit_Unlock(ped);
  2669. //
  2670. // Set dirty bit
  2671. //
  2672. ped->fDirty = TRUE;
  2673. return TRUE;
  2674. }
  2675. //---------------------------------------------------------------------------//
  2676. //
  2677. // Edit_DeleteText AorW
  2678. //
  2679. // Deletes the text between ped->ichMinSel and ped->ichMaxSel. The
  2680. // character at ichMaxSel is not deleted. But the character at ichMinSel is
  2681. // deleted. ped->cch is updated properly and memory is deallocated if enough
  2682. // text is removed. ped->ichMinSel, ped->ichMaxSel, and ped->ichCaret are set
  2683. // to point to the original ped->ichMinSel. Returns the number of characters
  2684. // deleted.
  2685. //
  2686. ICH Edit_DeleteText(PED ped)
  2687. {
  2688. PSTR pedText;
  2689. ICH cchDelete;
  2690. LPSTR lpDeleteSaveBuffer;
  2691. HANDLE hDeletedText;
  2692. DWORD bufferOffset;
  2693. cchDelete = ped->ichMaxSel - ped->ichMinSel;
  2694. if (cchDelete)
  2695. {
  2696. //
  2697. // Ok, now lets delete the text.
  2698. //
  2699. pedText = Edit_Lock(ped);
  2700. //
  2701. // Adjust UNDO fields so that we can undo this delete...
  2702. //
  2703. if (ped->undoType == UNDO_NONE)
  2704. {
  2705. UNDODELETEFROMSCRATCH:
  2706. if (ped->hDeletedText = GlobalAlloc(GPTR, (LONG)((cchDelete+1)*ped->cbChar)))
  2707. {
  2708. ped->undoType = UNDO_DELETE;
  2709. ped->ichDeleted = ped->ichMinSel;
  2710. ped->cchDeleted = cchDelete;
  2711. lpDeleteSaveBuffer = ped->hDeletedText;
  2712. RtlCopyMemory(lpDeleteSaveBuffer, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar);
  2713. lpDeleteSaveBuffer[cchDelete*ped->cbChar] = 0;
  2714. }
  2715. }
  2716. else if (ped->undoType & UNDO_INSERT)
  2717. {
  2718. UNDODELETE:
  2719. Edit_EmptyUndo(Pundo(ped));
  2720. ped->ichInsStart = ped->ichInsEnd = 0xFFFFFFFF;
  2721. ped->ichDeleted = 0xFFFFFFFF;
  2722. ped->cchDeleted = 0;
  2723. goto UNDODELETEFROMSCRATCH;
  2724. }
  2725. else if (ped->undoType == UNDO_DELETE)
  2726. {
  2727. if (ped->ichDeleted == ped->ichMaxSel)
  2728. {
  2729. //
  2730. // Copy deleted text to front of undo buffer
  2731. //
  2732. hDeletedText = GlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
  2733. if (!hDeletedText)
  2734. {
  2735. goto UNDODELETE;
  2736. }
  2737. bufferOffset = 0;
  2738. ped->ichDeleted = ped->ichMinSel;
  2739. }
  2740. else if (ped->ichDeleted == ped->ichMinSel)
  2741. {
  2742. //
  2743. // Copy deleted text to end of undo buffer
  2744. //
  2745. hDeletedText = GlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
  2746. if (!hDeletedText)
  2747. {
  2748. goto UNDODELETE;
  2749. }
  2750. bufferOffset = ped->cchDeleted*ped->cbChar;
  2751. }
  2752. else
  2753. {
  2754. //
  2755. // Clear the current UNDO delete and add the new one since
  2756. // the deletes aren't contiguous.
  2757. //
  2758. goto UNDODELETE;
  2759. }
  2760. ped->hDeletedText = hDeletedText;
  2761. lpDeleteSaveBuffer = (LPSTR)hDeletedText;
  2762. if (!bufferOffset)
  2763. {
  2764. //
  2765. // Move text in delete buffer up so that we can insert the next
  2766. // text at the head of the buffer.
  2767. //
  2768. RtlMoveMemory(lpDeleteSaveBuffer + cchDelete*ped->cbChar, lpDeleteSaveBuffer, ped->cchDeleted*ped->cbChar);
  2769. }
  2770. RtlCopyMemory(lpDeleteSaveBuffer + bufferOffset, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar);
  2771. lpDeleteSaveBuffer[(ped->cchDeleted + cchDelete)*ped->cbChar] = 0;
  2772. ped->cchDeleted += cchDelete;
  2773. }
  2774. if (ped->ichMaxSel != ped->cch)
  2775. {
  2776. //
  2777. // We are deleting text from the middle of the buffer so we have to
  2778. // shift text to the left.
  2779. //
  2780. RtlMoveMemory(pedText + ped->ichMinSel*ped->cbChar, pedText + ped->ichMaxSel*ped->cbChar, (ped->cch - ped->ichMaxSel)*ped->cbChar);
  2781. }
  2782. if (ped->cchAlloc - ped->cch > CCHALLOCEXTRA)
  2783. {
  2784. //
  2785. // Free some memory since we deleted a lot
  2786. //
  2787. LocalReAlloc(ped->hText, (DWORD)(ped->cch + (CCHALLOCEXTRA / 2))*ped->cbChar, LHND);
  2788. ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar;
  2789. }
  2790. ped->cch -= cchDelete;
  2791. if (ped->pLpkEditCallout)
  2792. {
  2793. HDC hdc;
  2794. hdc = Edit_GetDC(ped, TRUE);
  2795. ped->ichMinSel = ped->pLpkEditCallout->EditAdjustCaret((PED0)ped, hdc, pedText, ped->ichMinSel);
  2796. Edit_ReleaseDC(ped, hdc, TRUE);
  2797. }
  2798. ped->ichCaret = ped->ichMaxSel = ped->ichMinSel;
  2799. Edit_Unlock(ped);
  2800. //
  2801. // Set dirty bit
  2802. //
  2803. ped->fDirty = TRUE;
  2804. }
  2805. return cchDelete;
  2806. }
  2807. //---------------------------------------------------------------------------//
  2808. //
  2809. // Edit_NotifyParent AorW
  2810. //
  2811. // Sends the notification code to the parent of the edit control
  2812. //
  2813. VOID Edit_NotifyParent(PED ped, INT notificationCode)
  2814. {
  2815. //
  2816. // wParam is NotificationCode (hiword) and WindowID (loword)
  2817. // lParam is HWND of control sending the message
  2818. // Windows 95 checks for hwndParent != NULL before sending the message, but
  2819. // this is surely rare, and SendMessage NULL hwnd does nowt anyway (IanJa)
  2820. //
  2821. SendMessage(ped->hwndParent, WM_COMMAND,
  2822. (DWORD)MAKELONG(GetWindowID(ped->hwnd), notificationCode),
  2823. (LPARAM)ped->hwnd);
  2824. }
  2825. //---------------------------------------------------------------------------//
  2826. //
  2827. // Edit_SetClip AorW
  2828. //
  2829. // Sets the clip rect for the hdc to the formatting rectangle intersected
  2830. // with the client area.
  2831. //
  2832. VOID Edit_SetClip(PED ped, HDC hdc, BOOL fLeftMargin)
  2833. {
  2834. RECT rcClient;
  2835. RECT rcClip;
  2836. INT cxBorder;
  2837. INT cyBorder;
  2838. CopyRect(&rcClip, &ped->rcFmt);
  2839. if (ped->pLpkEditCallout)
  2840. {
  2841. //
  2842. // Complex script handling chooses whether to write margins later
  2843. //
  2844. rcClip.left -= ped->wLeftMargin;
  2845. rcClip.right += ped->wRightMargin;
  2846. }
  2847. else
  2848. {
  2849. //
  2850. // Should we consider the left margin?
  2851. //
  2852. if (fLeftMargin)
  2853. {
  2854. rcClip.left -= ped->wLeftMargin;
  2855. }
  2856. //
  2857. // Should we consider the right margin?
  2858. //
  2859. if (ped->fWrap)
  2860. {
  2861. rcClip.right += ped->wRightMargin;
  2862. }
  2863. }
  2864. //
  2865. // Set clip rectangle to rectClient intersect rectClip
  2866. // We must clip for single line edits also. -- B#1360
  2867. //
  2868. GetClientRect(ped->hwnd, &rcClient);
  2869. if (ped->fFlatBorder)
  2870. {
  2871. cxBorder = GetSystemMetrics(SM_CXBORDER);
  2872. cyBorder = GetSystemMetrics(SM_CYBORDER);
  2873. InflateRect(&rcClient, cxBorder, cyBorder);
  2874. }
  2875. IntersectRect(&rcClient, &rcClient, &rcClip);
  2876. IntersectClipRect(hdc,rcClient.left, rcClient.top,
  2877. rcClient.right, rcClient.bottom);
  2878. }
  2879. //---------------------------------------------------------------------------//
  2880. //
  2881. // Edit_GetDC AorW
  2882. //
  2883. // Hides the caret, gets the DC for the edit control, and clips to
  2884. // the rcFmt rectangle specified for the edit control and sets the proper
  2885. // font. If fFastDC, just select the proper font but don't bother about clip
  2886. // regions or hiding the caret.
  2887. //
  2888. HDC Edit_GetDC(PED ped, BOOL fFastDC)
  2889. {
  2890. HDC hdc;
  2891. if (!fFastDC)
  2892. {
  2893. HideCaret(ped->hwnd);
  2894. }
  2895. hdc = GetDC(ped->hwnd);
  2896. if (hdc != NULL)
  2897. {
  2898. Edit_SetClip(ped, hdc, (BOOL)(ped->xOffset == 0));
  2899. //
  2900. // Select the proper font for this edit control's dc.
  2901. //
  2902. if (ped->hFont)
  2903. {
  2904. SelectObject(hdc, ped->hFont);
  2905. }
  2906. }
  2907. return hdc;
  2908. }
  2909. //---------------------------------------------------------------------------//
  2910. //
  2911. // Edit_ReleaseDC AorW
  2912. //
  2913. // Releases the DC (hdc) for the edit control and shows the caret.
  2914. // If fFastDC, just select the proper font but don't bother about showing the
  2915. // caret.
  2916. //
  2917. VOID Edit_ReleaseDC(PED ped, HDC hdc, BOOL fFastDC)
  2918. {
  2919. //
  2920. // Restoring font not necessary
  2921. //
  2922. ReleaseDC(ped->hwnd, hdc);
  2923. if (!fFastDC)
  2924. {
  2925. ShowCaret(ped->hwnd);
  2926. }
  2927. }
  2928. //---------------------------------------------------------------------------//
  2929. //
  2930. // Edit_ResetTextInfo() AorW
  2931. //
  2932. // Handles a global change to the text by resetting text offsets, emptying
  2933. // the undo buffer, and rebuilding the lines
  2934. //
  2935. VOID Edit_ResetTextInfo(PED ped)
  2936. {
  2937. //
  2938. // Reset caret, selections, scrolling, and dirty information.
  2939. //
  2940. ped->iCaretLine = ped->ichCaret = 0;
  2941. ped->ichMinSel = ped->ichMaxSel = 0;
  2942. ped->xOffset = ped->ichScreenStart = 0;
  2943. ped->fDirty = FALSE;
  2944. Edit_EmptyUndo(Pundo(ped));
  2945. if (ped->fSingle)
  2946. {
  2947. if (!ped->listboxHwnd)
  2948. {
  2949. Edit_NotifyParent(ped, EN_UPDATE);
  2950. }
  2951. }
  2952. else
  2953. {
  2954. EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  2955. }
  2956. if (IsWindowVisible(ped->hwnd))
  2957. {
  2958. BOOL fErase;
  2959. if (ped->fSingle)
  2960. {
  2961. fErase = FALSE;
  2962. }
  2963. else
  2964. {
  2965. fErase = ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines);
  2966. }
  2967. //
  2968. // Always redraw whether or not the insert was successful. We might
  2969. // have NULL text. Paint() will check the redraw flag for us.
  2970. //
  2971. Edit_InvalidateClient(ped, fErase);
  2972. //
  2973. // BACKWARD COMPAT HACK: RAID expects the text to have been updated,
  2974. // so we have to do an UpdateWindow here. It moves an edit control
  2975. // around with fRedraw == FALSE, so it'll never get the paint message
  2976. // with the control in the right place.
  2977. //
  2978. if (!ped->fWin31Compat)
  2979. {
  2980. UpdateWindow(ped->hwnd);
  2981. }
  2982. }
  2983. if (ped->fSingle && !ped->listboxHwnd)
  2984. {
  2985. Edit_NotifyParent(ped, EN_CHANGE);
  2986. }
  2987. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  2988. }
  2989. //---------------------------------------------------------------------------//
  2990. //
  2991. // Edit_SetEditText AorW
  2992. //
  2993. // Copies the null terminated text in lpstr to the ped. Notifies the
  2994. // parent if there isn't enough memory. Sets the minsel, maxsel, and caret to
  2995. // the beginning of the inserted text. Returns TRUE if successful else FALSE
  2996. // if no memory (and notifies the parent).
  2997. //
  2998. BOOL Edit_SetEditText(PED ped, LPSTR lpstr)
  2999. {
  3000. ICH cchLength;
  3001. ICH cchSave = ped->cch;
  3002. ICH ichCaretSave = ped->ichCaret;
  3003. HWND hwndSave = ped->hwnd;
  3004. HANDLE hText;
  3005. ped->cch = ped->ichCaret = 0;
  3006. ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar;
  3007. if (!lpstr)
  3008. {
  3009. hText = LocalReAlloc(ped->hText, CCHALLOCEXTRA*ped->cbChar, LHND);
  3010. if (hText != NULL)
  3011. {
  3012. ped->hText = hText;
  3013. }
  3014. else
  3015. {
  3016. return FALSE;
  3017. }
  3018. }
  3019. else
  3020. {
  3021. cchLength = (ped->fAnsi ? strlen((LPSTR)lpstr) : wcslen((LPWSTR)lpstr));
  3022. //
  3023. // Add the text
  3024. //
  3025. if (cchLength && !Edit_InsertText(ped, lpstr, &cchLength))
  3026. {
  3027. //
  3028. // Restore original state and notify parent we ran out of memory.
  3029. //
  3030. ped->cch = cchSave;
  3031. ped->ichCaret = ichCaretSave;
  3032. Edit_NotifyParent(ped, EN_ERRSPACE);
  3033. return FALSE;
  3034. }
  3035. }
  3036. ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar;
  3037. if (IsWindow(hwndSave))
  3038. {
  3039. Edit_ResetTextInfo(ped);
  3040. }
  3041. return TRUE;
  3042. }
  3043. //---------------------------------------------------------------------------//
  3044. //
  3045. // Edit_InvalidateClient()
  3046. //
  3047. // Invalidates client of edit field. For old 3.x guys with borders,
  3048. // we draw it ourself (compatibility). So we don't want to invalidate
  3049. // the border or we'll get flicker.
  3050. //
  3051. VOID Edit_InvalidateClient(PED ped, BOOL fErase)
  3052. {
  3053. if (ped->fFlatBorder)
  3054. {
  3055. RECT rcT;
  3056. INT cxBorder;
  3057. INT cyBorder;
  3058. GetClientRect(ped->hwnd, &rcT);
  3059. cxBorder = GetSystemMetrics(SM_CXBORDER);
  3060. cyBorder = GetSystemMetrics(SM_CYBORDER);
  3061. InflateRect(&rcT, cxBorder, cyBorder);
  3062. InvalidateRect(ped->hwnd, &rcT, fErase);
  3063. }
  3064. else
  3065. {
  3066. InvalidateRect(ped->hwnd, NULL, fErase);
  3067. }
  3068. }
  3069. //---------------------------------------------------------------------------//
  3070. //
  3071. // Edit_Copy AorW
  3072. //
  3073. // Copies the text between ichMinSel and ichMaxSel to the clipboard.
  3074. // Returns the number of characters copied.
  3075. //
  3076. ICH Edit_Copy(PED ped)
  3077. {
  3078. HANDLE hData;
  3079. char *pchSel;
  3080. char *lpchClip;
  3081. ICH cbData;
  3082. //
  3083. // Don't allow copies from password style controls
  3084. //
  3085. if (ped->charPasswordChar)
  3086. {
  3087. Edit_ShowBalloonTipWrap(ped->hwnd, IDS_PASSWORDCUT_TITLE, IDS_PASSWORDCUT_MSG, TTI_ERROR);
  3088. MessageBeep(0);
  3089. return 0;
  3090. }
  3091. cbData = (ped->ichMaxSel - ped->ichMinSel) * ped->cbChar;
  3092. if (!cbData)
  3093. {
  3094. return 0;
  3095. }
  3096. if (!OpenClipboard(ped->hwnd))
  3097. {
  3098. return 0;
  3099. }
  3100. EmptyClipboard();
  3101. hData = GlobalAlloc(LHND, (LONG)(cbData + ped->cbChar));
  3102. if (!hData)
  3103. {
  3104. CloseClipboard();
  3105. return 0;
  3106. }
  3107. lpchClip = GlobalLock(hData);
  3108. UserAssert(lpchClip);
  3109. pchSel = Edit_Lock(ped);
  3110. pchSel = pchSel + (ped->ichMinSel * ped->cbChar);
  3111. RtlCopyMemory(lpchClip, pchSel, cbData);
  3112. if (ped->fAnsi)
  3113. {
  3114. *(lpchClip + cbData) = 0;
  3115. }
  3116. else
  3117. {
  3118. *(LPWSTR)(lpchClip + cbData) = (WCHAR)0;
  3119. }
  3120. Edit_Unlock(ped);
  3121. GlobalUnlock(hData);
  3122. SetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT, hData);
  3123. CloseClipboard();
  3124. return cbData;
  3125. }
  3126. //---------------------------------------------------------------------------//
  3127. LRESULT Edit_TrackBalloonTip(PED ped)
  3128. {
  3129. if (ped->hwndBalloon)
  3130. {
  3131. DWORD dwPackedCoords;
  3132. HDC hdc = Edit_GetDC(ped, TRUE);
  3133. RECT rcWindow;
  3134. POINT pt;
  3135. int cxCharOffset = TESTFLAG(GET_EXSTYLE(ped), WS_EX_RTLREADING) ? -ped->aveCharWidth : ped->aveCharWidth;
  3136. //
  3137. // Get the caret position
  3138. //
  3139. if (ped->fSingle)
  3140. {
  3141. pt.x = EditSL_IchToLeftXPos(ped, hdc, ped->ichCaret) + cxCharOffset;
  3142. pt.y = ped->rcFmt.bottom;
  3143. }
  3144. else
  3145. {
  3146. EditML_IchToXYPos(ped, hdc, ped->ichCaret, FALSE, &pt);
  3147. pt.x += cxCharOffset;
  3148. pt.y += ped->lineHeight;
  3149. }
  3150. //
  3151. // Translate to window coords
  3152. //
  3153. GetWindowRect(ped->hwnd, &rcWindow);
  3154. pt.x += rcWindow.left;
  3155. pt.y += rcWindow.top;
  3156. //
  3157. // Position the tip stem at the caret position
  3158. //
  3159. dwPackedCoords = (DWORD) MAKELONG(pt.x, pt.y);
  3160. SendMessage(ped->hwndBalloon, TTM_TRACKPOSITION, 0, (LPARAM) dwPackedCoords);
  3161. Edit_ReleaseDC(ped, hdc, TRUE);
  3162. return 1;
  3163. }
  3164. return 0;
  3165. }
  3166. //---------------------------------------------------------------------------//
  3167. LRESULT CALLBACK Edit_BalloonTipParentSubclassProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData)
  3168. {
  3169. PED ped = (PED)dwRefData;
  3170. switch (uMessage)
  3171. {
  3172. case WM_MOVE:
  3173. case WM_SIZING:
  3174. //
  3175. // dismiss any showing tips
  3176. //
  3177. Edit_HideBalloonTip(ped->hwnd);
  3178. break;
  3179. case WM_DESTROY:
  3180. // Clean up subclass
  3181. RemoveWindowSubclass(hDlg, Edit_BalloonTipParentSubclassProc, (UINT_PTR) ped->hwnd);
  3182. break;
  3183. default:
  3184. break;
  3185. }
  3186. return DefSubclassProc(hDlg, uMessage, wParam, lParam);
  3187. }
  3188. //---------------------------------------------------------------------------//
  3189. LRESULT Edit_BalloonTipSubclassParents(PED ped)
  3190. {
  3191. // Subclass all windows along the parent chain from the edit control
  3192. // and in the same thread (can only subclass windows with same thread affinity)
  3193. HWND hwndParent = GetAncestor(ped->hwnd, GA_PARENT);
  3194. DWORD dwTid = GetWindowThreadProcessId(ped->hwnd, NULL);
  3195. while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL)))
  3196. {
  3197. SetWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR)ped->hwnd, (DWORD_PTR)ped);
  3198. hwndParent = GetAncestor(hwndParent, GA_PARENT);
  3199. }
  3200. return TRUE;
  3201. }
  3202. //---------------------------------------------------------------------------//
  3203. HWND Edit_BalloonTipRemoveSubclasses(PED ped)
  3204. {
  3205. HWND hwndParent = GetAncestor(ped->hwnd, GA_PARENT);
  3206. HWND hwndTopMost = NULL;
  3207. DWORD dwTid = GetWindowThreadProcessId(ped->hwnd, NULL);
  3208. while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL)))
  3209. {
  3210. RemoveWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR) ped->hwnd);
  3211. hwndTopMost = hwndParent;
  3212. hwndParent = GetAncestor(hwndParent, GA_PARENT);
  3213. }
  3214. return hwndTopMost;
  3215. }
  3216. //---------------------------------------------------------------------------//
  3217. LRESULT Edit_HideBalloonTipHandler(PED ped)
  3218. {
  3219. if (ped->hwndBalloon)
  3220. {
  3221. HWND hwndParent;
  3222. KillTimer(ped->hwnd, ID_EDITTIMER);
  3223. SendMessage(ped->hwndBalloon, TTM_TRACKACTIVATE, FALSE, 0);
  3224. DestroyWindow(ped->hwndBalloon);
  3225. ped->hwndBalloon = NULL;
  3226. hwndParent = Edit_BalloonTipRemoveSubclasses(ped);
  3227. if (hwndParent && IsWindow(hwndParent))
  3228. {
  3229. InvalidateRect(hwndParent, NULL, TRUE);
  3230. UpdateWindow(hwndParent);
  3231. }
  3232. if (hwndParent != ped->hwnd)
  3233. {
  3234. RedrawWindow(ped->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
  3235. }
  3236. }
  3237. return TRUE;
  3238. }
  3239. //---------------------------------------------------------------------------//
  3240. __inline LRESULT Edit_ShowBalloonTipWrap(HWND hwnd, DWORD dwTitleId, DWORD dwMsgId, DWORD dwIconId)
  3241. {
  3242. WCHAR szTitle[56];
  3243. WCHAR szMsg[MAX_PATH];
  3244. EDITBALLOONTIP ebt;
  3245. LoadString(HINST_THISDLL, dwTitleId, szTitle, ARRAYSIZE(szTitle));
  3246. LoadString(HINST_THISDLL, dwMsgId, szMsg, ARRAYSIZE(szMsg));
  3247. ebt.cbStruct = sizeof(ebt);
  3248. ebt.pszTitle = szTitle;
  3249. ebt.pszText = szMsg;
  3250. ebt.ttiIcon = dwIconId;
  3251. return Edit_ShowBalloonTip(hwnd, &ebt);
  3252. }
  3253. //---------------------------------------------------------------------------//
  3254. LRESULT Edit_ShowBalloonTipHandler(PED ped, PEDITBALLOONTIP pebt)
  3255. {
  3256. LRESULT lResult = FALSE;
  3257. Edit_HideBalloonTipHandler(ped);
  3258. if (sizeof(EDITBALLOONTIP) == pebt->cbStruct)
  3259. {
  3260. ped->hwndBalloon = CreateWindowEx(
  3261. (IS_BIDI_LOCALIZED_SYSTEM() ? WS_EX_LAYOUTRTL : 0),
  3262. TOOLTIPS_CLASS, NULL,
  3263. WS_POPUP | TTS_NOPREFIX | TTS_BALLOON,
  3264. CW_USEDEFAULT, CW_USEDEFAULT,
  3265. CW_USEDEFAULT, CW_USEDEFAULT,
  3266. ped->hwnd, NULL, g_hinst,
  3267. NULL);
  3268. if (NULL != ped->hwndBalloon)
  3269. {
  3270. TOOLINFO ti = {0};
  3271. ti.cbSize = TTTOOLINFOW_V2_SIZE;
  3272. ti.uFlags = TTF_IDISHWND | TTF_TRACK;
  3273. ti.hwnd = ped->hwnd;
  3274. ti.uId = (WPARAM)1;
  3275. ti.lpszText = (LPWSTR)pebt->pszText;
  3276. SendMessage(ped->hwndBalloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
  3277. SendMessage(ped->hwndBalloon, TTM_SETMAXTIPWIDTH, 0, 300);
  3278. SendMessage(ped->hwndBalloon, TTM_SETTITLE, (WPARAM) pebt->ttiIcon, (LPARAM)pebt->pszTitle);
  3279. Edit_TrackBalloonTip(ped);
  3280. SendMessage(ped->hwndBalloon, TTM_TRACKACTIVATE, (WPARAM) TRUE, (LPARAM)&ti);
  3281. SetFocus(ped->hwnd);
  3282. Edit_BalloonTipSubclassParents(ped);
  3283. //
  3284. // set timeout to kill the tip
  3285. //
  3286. KillTimer(ped->hwnd, ID_EDITTIMER);
  3287. SetTimer(ped->hwnd, ID_EDITTIMER, EDIT_TIPTIMEOUT, NULL);
  3288. lResult = TRUE;
  3289. }
  3290. }
  3291. return lResult;
  3292. }
  3293. //---------------------------------------------------------------------------//
  3294. BOOL Edit_ClientEdgePaint(PED ped, HRGN hRgnUpdate)
  3295. {
  3296. HDC hdc;
  3297. BOOL bRet = FALSE;
  3298. hdc = (hRgnUpdate != NULL) ?
  3299. GetDCEx(ped->hwnd,
  3300. hRgnUpdate,
  3301. DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE | DCX_INTERSECTRGN | DCX_NODELETERGN) :
  3302. GetDCEx(ped->hwnd,
  3303. NULL,
  3304. DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE);
  3305. if (hdc)
  3306. {
  3307. HBRUSH hbr;
  3308. BOOL fDeleteBrush = FALSE;
  3309. hbr = Edit_GetBrush(ped, hdc, &fDeleteBrush);
  3310. if (hbr)
  3311. {
  3312. RECT rc;
  3313. HRGN hrgn;
  3314. INT iStateId = Edit_GetStateId(ped);
  3315. INT cxBorder = 0, cyBorder = 0;
  3316. if (SUCCEEDED(GetThemeInt(ped->hTheme, EP_EDITTEXT, iStateId, TMT_SIZINGBORDERWIDTH, &cxBorder)))
  3317. {
  3318. cyBorder = cxBorder;
  3319. }
  3320. else
  3321. {
  3322. cxBorder = g_cxBorder;
  3323. cyBorder = g_cyBorder;
  3324. }
  3325. GetWindowRect(ped->hwnd, &rc);
  3326. //
  3327. // Create an update region without the client edge
  3328. // to pass to DefWindowProc
  3329. //
  3330. InflateRect(&rc, -g_cxEdge, -g_cyEdge);
  3331. hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  3332. if (hRgnUpdate != NULL)
  3333. {
  3334. CombineRgn(hrgn, hRgnUpdate, hrgn, RGN_AND);
  3335. }
  3336. //
  3337. // Zero-origin the rect
  3338. //
  3339. OffsetRect(&rc, -rc.left, -rc.top);
  3340. //
  3341. // clip our drawing to the non-client edge
  3342. //
  3343. OffsetRect(&rc, g_cxEdge, g_cyEdge);
  3344. ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  3345. InflateRect(&rc, g_cxEdge, g_cyEdge);
  3346. DrawThemeBackground(ped->hTheme, hdc, EP_EDITTEXT, iStateId, &rc, 0);
  3347. //
  3348. // Fill with the control's brush first since the ThemeBackground
  3349. // border may not be as thick as the client edge
  3350. //
  3351. if ((cxBorder < g_cxEdge) && (cyBorder < g_cyEdge))
  3352. {
  3353. InflateRect(&rc, cxBorder-g_cxEdge, cyBorder-g_cyEdge);
  3354. FillRect(hdc, &rc, hbr);
  3355. }
  3356. DefWindowProc(ped->hwnd, WM_NCPAINT, (WPARAM)hrgn, 0);
  3357. DeleteObject(hrgn);
  3358. if (fDeleteBrush)
  3359. {
  3360. DeleteObject(hbr);
  3361. }
  3362. bRet = TRUE;
  3363. }
  3364. ReleaseDC(ped->hwnd, hdc);
  3365. }
  3366. return bRet;
  3367. }
  3368. //---------------------------------------------------------------------------//
  3369. //
  3370. // Edit_WndProc
  3371. //
  3372. // WndProc for all edit controls.
  3373. // Dispatches all messages to the appropriate handlers which are named
  3374. // as follows:
  3375. // EditSL_ (single line) prefixes all single line edit control
  3376. // EditML_ (multi line) prefixes all multi- line edit controls
  3377. // Edit_ (edit control) prefixes all common handlers
  3378. //
  3379. // The Edit_WndProc only handles messages common to both single and multi
  3380. // line edit controls. Messages which are handled differently between
  3381. // single and multi are sent to EditSL_WndProc or EditML_WndProc.
  3382. //
  3383. LRESULT Edit_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3384. {
  3385. PED ped;
  3386. LRESULT lResult;
  3387. //
  3388. // Get the instance data for this edit control
  3389. //
  3390. ped = Edit_GetPtr(hwnd);
  3391. if (!ped && uMsg != WM_NCCREATE)
  3392. {
  3393. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  3394. }
  3395. //
  3396. // Dispatch the various messages we can receive
  3397. //
  3398. lResult = 1L;
  3399. switch (uMsg)
  3400. {
  3401. //
  3402. // Messages which are handled the same way for both single and multi line
  3403. // edit controls.
  3404. //
  3405. case WM_KEYDOWN:
  3406. //
  3407. // LPK handling of Ctrl/LShift, Ctrl/RShift
  3408. //
  3409. if (ped && ped->pLpkEditCallout && ped->fAllowRTL)
  3410. {
  3411. //
  3412. // Any keydown cancels a ctrl/shift reading order change
  3413. //
  3414. ped->fSwapRoOnUp = FALSE;
  3415. switch (wParam)
  3416. {
  3417. case VK_SHIFT:
  3418. if ((GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
  3419. {
  3420. //
  3421. // Left shift or right shift pressed while control held down
  3422. // Check that alt (VK_MENU) isn't down to avoid false firing
  3423. // on AltGr which equals Ctrl+Alt.
  3424. //
  3425. if (MapVirtualKey((LONG)lParam>>16&0xff, 3) == VK_LSHIFT)
  3426. {
  3427. //
  3428. // User wants left to right reading order
  3429. //
  3430. ped->fSwapRoOnUp = (ped->fRtoLReading) || (ped->format & ES_RIGHT);
  3431. ped->fLShift = TRUE;
  3432. }
  3433. else
  3434. {
  3435. //
  3436. // User wants right to left reading order
  3437. //
  3438. ped->fSwapRoOnUp = (!ped->fRtoLReading) || (ped->format & ES_RIGHT);
  3439. ped->fLShift = FALSE;
  3440. }
  3441. }
  3442. break;
  3443. case VK_LEFT:
  3444. if (ped->fRtoLReading)
  3445. {
  3446. wParam = VK_RIGHT;
  3447. }
  3448. break;
  3449. case VK_RIGHT:
  3450. if (ped->fRtoLReading)
  3451. {
  3452. wParam = VK_LEFT;
  3453. }
  3454. break;
  3455. }
  3456. }
  3457. goto HandleEditMsg;
  3458. case WM_KEYUP:
  3459. if (ped && ped->pLpkEditCallout && ped->fAllowRTL && ped->fSwapRoOnUp)
  3460. {
  3461. BOOL fReadingOrder;
  3462. //
  3463. // Complete reading order change detected earlier during keydown
  3464. //
  3465. ped->fSwapRoOnUp = FALSE;
  3466. fReadingOrder = ped->fRtoLReading;
  3467. //
  3468. // Remove any overriding ES_CENTRE or ES_RIGHT format from dwStyle
  3469. //
  3470. SetWindowLong(hwnd, GWL_STYLE, (GET_STYLE(ped) & ~ES_FMTMASK));
  3471. if (ped->fLShift)
  3472. {
  3473. //
  3474. // Set Left to Right reading order and right scrollbar in EX_STYLE
  3475. //
  3476. SetWindowLong(hwnd, GWL_EXSTYLE, (GET_EXSTYLE(ped) & ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR)));
  3477. //
  3478. // Edit control is LTR now, then notify the parent.
  3479. //
  3480. Edit_NotifyParent(ped, EN_ALIGN_LTR_EC);
  3481. //
  3482. // ? Select a keyboard layout appropriate to LTR operation
  3483. //
  3484. }
  3485. else
  3486. {
  3487. //
  3488. // Set Right to Left reading order, right alignment and left scrollbar
  3489. //
  3490. SetWindowLong(hwnd,
  3491. GWL_EXSTYLE,
  3492. GET_EXSTYLE(ped) | WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
  3493. //
  3494. // Edit control is RTL now, then notify the parent.
  3495. //
  3496. Edit_NotifyParent(ped, EN_ALIGN_RTL_EC);
  3497. //
  3498. // ? Select a keyboard layout appropriate to RTL operation
  3499. //
  3500. }
  3501. //
  3502. // If reading order didn't change, so we are sure the alignment
  3503. // changed and the edit window didn't invalidate yet.
  3504. //
  3505. if (fReadingOrder == (BOOL) ped->fRtoLReading)
  3506. {
  3507. Edit_InvalidateClient(ped, TRUE);
  3508. }
  3509. }
  3510. goto HandleEditMsg;
  3511. case WM_INPUTLANGCHANGE:
  3512. if (ped)
  3513. {
  3514. //
  3515. // EC_INSERT_COMPOSITION_CHAR : WM_INPUTLANGCHANGE - call Edit_InitInsert()
  3516. //
  3517. HKL hkl = GetKeyboardLayout(0);
  3518. Edit_InitInsert(ped, hkl);
  3519. if (ped->fInReconversion)
  3520. {
  3521. Edit_InOutReconversionMode(ped, FALSE);
  3522. }
  3523. //
  3524. // Font and caret position might be changed while
  3525. // another keyboard layout is active. Set those
  3526. // if the edit control has the focus.
  3527. //
  3528. if (ped->fFocus && ImmIsIME(hkl))
  3529. {
  3530. POINT pt;
  3531. Edit_SetCompositionFont(ped);
  3532. GetCaretPos(&pt);
  3533. Edit_ImmSetCompositionWindow(ped, pt.x, pt.y);
  3534. }
  3535. }
  3536. goto HandleEditMsg;
  3537. case WM_COPY:
  3538. //
  3539. // wParam - not used
  3540. // lParam - not used
  3541. //
  3542. lResult = (LONG)Edit_Copy(ped);
  3543. break;
  3544. case WM_CUT:
  3545. //
  3546. // wParam -- not used
  3547. // lParam -- not used
  3548. //
  3549. Edit_CutText(ped);
  3550. lResult = 0;
  3551. break;
  3552. case WM_CLEAR:
  3553. //
  3554. // wParam - not used
  3555. // lParam - not used
  3556. //
  3557. Edit_ClearText(ped);
  3558. lResult = 0;
  3559. break;
  3560. case WM_ENABLE:
  3561. //
  3562. // wParam - nonzero if window is enabled else disable window if 0.
  3563. // lParam - not used
  3564. //
  3565. ped->fDisabled = !((BOOL)wParam);
  3566. CCInvalidateFrame(hwnd);
  3567. Edit_InvalidateClient(ped, TRUE);
  3568. lResult = (LONG)ped->fDisabled;
  3569. break;
  3570. case WM_SYSCHAR:
  3571. //
  3572. // wParam - key value
  3573. // lParam - not used
  3574. //
  3575. //
  3576. // If this is a WM_SYSCHAR message generated by the UNDO
  3577. // keystroke we want to EAT IT
  3578. //
  3579. if ((lParam & SYS_ALTERNATE) &&
  3580. ((WORD)wParam == VK_BACK))
  3581. {
  3582. lResult = TRUE;
  3583. }
  3584. else
  3585. {
  3586. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  3587. }
  3588. break;
  3589. case EM_GETLINECOUNT:
  3590. //
  3591. // wParam - not used
  3592. // lParam - not used
  3593. //
  3594. lResult = (LONG)ped->cLines;
  3595. break;
  3596. case EM_GETMODIFY:
  3597. //
  3598. // wParam - not used
  3599. // lParam - not used
  3600. //
  3601. //
  3602. // Gets the state of the modify flag for this edit control.
  3603. //
  3604. lResult = (LONG)ped->fDirty;
  3605. break;
  3606. case EM_SETMODIFY:
  3607. //
  3608. // wParam - specifies the new value for the modify flag
  3609. // lParam - not used
  3610. //
  3611. //
  3612. // Sets the state of the modify flag for
  3613. // this edit control.
  3614. //
  3615. ped->fDirty = (wParam != 0);
  3616. break;
  3617. case EM_GETRECT:
  3618. //
  3619. // wParam - not used
  3620. // lParam - pointer to a RECT data structure that gets the dimensions.
  3621. //
  3622. //
  3623. // Copies the rcFmt rect to *lpRect.
  3624. //
  3625. CopyRect((LPRECT)lParam, (LPRECT)&ped->rcFmt);
  3626. lResult = (LONG)TRUE;
  3627. break;
  3628. case WM_GETFONT:
  3629. //
  3630. // wParam - not used
  3631. // lParam - not used
  3632. //
  3633. lResult = (LRESULT)ped->hFont;
  3634. break;
  3635. case WM_SETFONT:
  3636. //
  3637. // wParam - handle to the font
  3638. // lParam - redraw if true else don't
  3639. //
  3640. Edit_SetFont(ped, (HANDLE)wParam, (BOOL)LOWORD(lParam));
  3641. break;
  3642. case WM_GETTEXT:
  3643. //
  3644. // wParam - max number of _bytes_ (not characters) to copy
  3645. // lParam - buffer to copy text to. Text is 0 terminated.
  3646. //
  3647. lResult = (LRESULT)Edit_GetTextHandler(ped, (ICH)wParam, (LPSTR)lParam, TRUE);
  3648. break;
  3649. case WM_SETTEXT:
  3650. //
  3651. // wParam - not used
  3652. // lParam - LPSTR, null-terminated, with new text.
  3653. //
  3654. lResult = (LRESULT)Edit_SetEditText(ped, (LPSTR)lParam);
  3655. break;
  3656. case WM_GETTEXTLENGTH:
  3657. //
  3658. // Return count of CHARs!!!
  3659. //
  3660. lResult = (LONG)ped->cch;
  3661. break;
  3662. case WM_DESTROY:
  3663. //
  3664. // Make sure we unsubclass for the balloon tip, if appropriate
  3665. //
  3666. lResult = Edit_HideBalloonTipHandler(ped);
  3667. break;
  3668. case WM_NCDESTROY:
  3669. case WM_FINALDESTROY:
  3670. //
  3671. // wParam - not used
  3672. // lParam - not used
  3673. //
  3674. Edit_NcDestroyHandler(hwnd, ped);
  3675. lResult = 0;
  3676. break;
  3677. case WM_RBUTTONDOWN:
  3678. //
  3679. // Most apps (i.e. everyone but Quicken) don't pass on the rbutton
  3680. // messages when they do something with 'em inside of subclassed
  3681. // edit fields. As such, we keep track of whether we saw the
  3682. // down before the up. If we don't see the up, then DefWindowProc
  3683. // won't generate the context menu message, so no big deal. If
  3684. // we didn't see the down, then don't let WM_CONTEXTMENU do
  3685. // anything.
  3686. //
  3687. // We also might want to not generate WM_CONTEXTMENUs for old
  3688. // apps when the mouse is captured.
  3689. //
  3690. ped->fSawRButtonDown = TRUE;
  3691. goto HandleEditMsg;
  3692. case WM_RBUTTONUP:
  3693. if (ped->fSawRButtonDown)
  3694. {
  3695. ped->fSawRButtonDown = FALSE;
  3696. if (!ped->fInReconversion)
  3697. {
  3698. goto HandleEditMsg;
  3699. }
  3700. }
  3701. //
  3702. // Don't pass this on to DWP so WM_CONTEXTMENU isn't generated.
  3703. //
  3704. lResult = 0;
  3705. break;
  3706. case WM_CONTEXTMENU:
  3707. {
  3708. POINT pt;
  3709. INT nHit = (INT)DefWindowProc(hwnd, WM_NCHITTEST, 0, lParam);
  3710. if ((nHit == HTVSCROLL) || (nHit == HTHSCROLL))
  3711. {
  3712. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  3713. }
  3714. else
  3715. {
  3716. POINTSTOPOINT(pt, lParam);
  3717. if (!TESTFLAG(GET_STATE2(ped), WS_S2_OLDUI) && Edit_IsAncestorActive(hwnd))
  3718. {
  3719. Edit_Menu(hwnd, ped, &pt);
  3720. }
  3721. lResult = 0;
  3722. }
  3723. break;
  3724. }
  3725. case EM_CANUNDO:
  3726. //
  3727. // wParam - not used
  3728. // lParam - not used
  3729. //
  3730. lResult = (LONG)(ped->undoType != UNDO_NONE);
  3731. break;
  3732. case EM_EMPTYUNDOBUFFER:
  3733. //
  3734. // wParam - not used
  3735. // lParam - not used
  3736. //
  3737. Edit_EmptyUndo(Pundo(ped));
  3738. break;
  3739. case EM_GETMARGINS:
  3740. //
  3741. // wParam - not used
  3742. // lParam - not used
  3743. //
  3744. lResult = MAKELONG(ped->wLeftMargin, ped->wRightMargin);
  3745. break;
  3746. case EM_SETMARGINS:
  3747. //
  3748. // wParam - EC_ margin flags
  3749. // lParam - LOWORD is left, HIWORD is right margin
  3750. //
  3751. Edit_SetMargin(ped, (UINT)wParam, (DWORD)lParam, TRUE);
  3752. lResult = 0;
  3753. break;
  3754. case EM_GETSEL:
  3755. //
  3756. // Gets the selection range for the given edit control. The
  3757. // starting position is in the low order word. It contains the position
  3758. // of the first nonselected character after the end of the selection in
  3759. // the high order word.
  3760. //
  3761. if ((PDWORD)wParam != NULL)
  3762. {
  3763. *((PDWORD)wParam) = ped->ichMinSel;
  3764. }
  3765. if ((PDWORD)lParam != NULL)
  3766. {
  3767. *((PDWORD)lParam) = ped->ichMaxSel;
  3768. }
  3769. lResult = MAKELONG(ped->ichMinSel,ped->ichMaxSel);
  3770. break;
  3771. case EM_GETLIMITTEXT:
  3772. //
  3773. // wParam - not used
  3774. // lParam - not used
  3775. //
  3776. lResult = ped->cchTextMax;
  3777. break;
  3778. case EM_SETLIMITTEXT:
  3779. //
  3780. // wParam - max number of CHARACTERS that can be entered
  3781. // lParam - not used
  3782. //
  3783. //
  3784. // Specifies the maximum number of characters of text the user may
  3785. // enter. If maxLength is 0, we may enter MAXINT number of CHARACTERS.
  3786. //
  3787. if (ped->fSingle)
  3788. {
  3789. if (wParam)
  3790. {
  3791. wParam = min(0x7FFFFFFEu, wParam);
  3792. }
  3793. else
  3794. {
  3795. wParam = 0x7FFFFFFEu;
  3796. }
  3797. }
  3798. if (wParam)
  3799. {
  3800. ped->cchTextMax = (ICH)wParam;
  3801. }
  3802. else
  3803. {
  3804. ped->cchTextMax = 0xFFFFFFFFu;
  3805. }
  3806. break;
  3807. case EM_POSFROMCHAR:
  3808. //
  3809. // Validate that char index is within text range
  3810. //
  3811. if (wParam >= ped->cch)
  3812. {
  3813. lResult = -1L;
  3814. }
  3815. else
  3816. {
  3817. goto HandleEditMsg;
  3818. }
  3819. break;
  3820. case EM_CHARFROMPOS:
  3821. {
  3822. //
  3823. // Validate that point is within client of edit field
  3824. //
  3825. RECT rc;
  3826. POINT pt;
  3827. POINTSTOPOINT(pt, lParam);
  3828. GetClientRect(hwnd, &rc);
  3829. if (!PtInRect(&rc, pt))
  3830. {
  3831. lResult = -1L;
  3832. }
  3833. else
  3834. {
  3835. goto HandleEditMsg;
  3836. }
  3837. break;
  3838. }
  3839. case EM_SETPASSWORDCHAR:
  3840. //
  3841. // wParam - sepecifies the new char to display instead of the
  3842. // real text. if null, display the real text.
  3843. //
  3844. Edit_SetPasswordCharHandler(ped, (UINT)wParam);
  3845. break;
  3846. case EM_GETPASSWORDCHAR:
  3847. lResult = (DWORD)ped->charPasswordChar;
  3848. break;
  3849. case EM_SETREADONLY:
  3850. //
  3851. // wParam - state to set read only flag to
  3852. //
  3853. ped->fReadOnly = (wParam != 0);
  3854. if (wParam)
  3855. {
  3856. SetWindowState(hwnd, ES_READONLY);
  3857. }
  3858. else
  3859. {
  3860. ClearWindowState(hwnd, ES_READONLY);
  3861. }
  3862. lResult = 1L;
  3863. if ( g_fIMMEnabled )
  3864. {
  3865. Edit_EnableDisableIME( ped );
  3866. }
  3867. //
  3868. // We need to redraw the edit field so that the background color
  3869. // changes. Read-only edits are drawn in CTLCOLOR_STATIC while
  3870. // others are drawn with CTLCOLOR_EDIT.
  3871. //
  3872. Edit_InvalidateClient(ped, TRUE);
  3873. break;
  3874. case EM_SETWORDBREAKPROC:
  3875. // wParam - not used
  3876. // lParam - PROC address of an app supplied call back function
  3877. ped->lpfnNextWord = (EDITWORDBREAKPROCA)lParam;
  3878. break;
  3879. case EM_GETWORDBREAKPROC:
  3880. lResult = (LRESULT)ped->lpfnNextWord;
  3881. break;
  3882. case EM_GETIMESTATUS:
  3883. //
  3884. // wParam == sub command
  3885. //
  3886. if (wParam == EMSIS_COMPOSITIONSTRING)
  3887. {
  3888. lResult = ped->wImeStatus;
  3889. }
  3890. break;
  3891. case EM_SETIMESTATUS:
  3892. //
  3893. // wParam == sub command
  3894. //
  3895. if (wParam == EMSIS_COMPOSITIONSTRING)
  3896. {
  3897. ped->wImeStatus = (WORD)lParam;
  3898. }
  3899. break;
  3900. case WM_NCCREATE:
  3901. ped = (PED)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(ED));
  3902. if (ped)
  3903. {
  3904. //
  3905. // Success... store the instance pointer.
  3906. //
  3907. TraceMsg(TF_STANDARD, "EDIT: Setting edit instance pointer.");
  3908. Edit_SetPtr(hwnd, ped);
  3909. lResult = Edit_NcCreate(ped, hwnd, (LPCREATESTRUCT)lParam);
  3910. }
  3911. else
  3912. {
  3913. //
  3914. // Failed... return FALSE.
  3915. //
  3916. // From a WM_NCCREATE msg, this will cause the
  3917. // CreateWindow call to fail.
  3918. //
  3919. TraceMsg(TF_STANDARD, "EDIT: Unable to allocate edit instance structure.");
  3920. lResult = FALSE;
  3921. }
  3922. break;
  3923. case WM_LBUTTONDOWN:
  3924. //
  3925. // B#3623
  3926. // Don't set focus to edit field if it is within an inactive,
  3927. // captioned child.
  3928. // We might want to version switch this... I haven't found
  3929. // any problems by not, but you never know...
  3930. //
  3931. if (Edit_IsAncestorActive(hwnd))
  3932. {
  3933. //
  3934. // Reconversion support: quit reconversion if left button is clicked.
  3935. // Otherwise, if the current KL is Korean, finailize the composition string.
  3936. //
  3937. if (ped->fInReconversion || ped->fKorea)
  3938. {
  3939. BOOLEAN fReconversion = (BOOLEAN)ped->fInReconversion;
  3940. DWORD dwIndex = fReconversion ? CPS_CANCEL : CPS_COMPLETE;
  3941. HIMC hImc;
  3942. ped->fReplaceCompChr = FALSE;
  3943. hImc = ImmGetContext(ped->hwnd);
  3944. if (hImc)
  3945. {
  3946. ImmNotifyIME(hImc, NI_COMPOSITIONSTR, dwIndex, 0);
  3947. ImmReleaseContext(ped->hwnd, hImc);
  3948. }
  3949. if (fReconversion)
  3950. {
  3951. Edit_InOutReconversionMode(ped, FALSE);
  3952. }
  3953. Edit_SetCaretHandler(ped);
  3954. }
  3955. goto HandleEditMsg;
  3956. }
  3957. break;
  3958. case WM_MOUSELEAVE:
  3959. if (ped->hTheme && ped->fHot)
  3960. {
  3961. ped->fHot = FALSE;
  3962. SendMessage(ped->hwnd, WM_NCPAINT, 1, 0);
  3963. }
  3964. break;
  3965. case WM_MOUSEMOVE:
  3966. //
  3967. // If the hot bit is not already set
  3968. // and we are themed
  3969. //
  3970. if (ped->hTheme && !ped->fHot)
  3971. {
  3972. TRACKMOUSEEVENT tme;
  3973. //
  3974. // Set the hot bit and request that
  3975. // we be notified when the mouse leaves
  3976. //
  3977. ped->fHot = TRUE;
  3978. tme.cbSize = sizeof(tme);
  3979. tme.dwFlags = TME_LEAVE;
  3980. tme.hwndTrack = ped->hwnd;
  3981. tme.dwHoverTime = 0;
  3982. TrackMouseEvent(&tme);
  3983. SendMessage(ped->hwnd, WM_NCPAINT, 1, 0);
  3984. }
  3985. //
  3986. // We only care about mouse messages when mouse is down.
  3987. //
  3988. if (ped->fMouseDown)
  3989. {
  3990. goto HandleEditMsg;
  3991. }
  3992. break;
  3993. case WM_NCPAINT:
  3994. //
  3995. // Draw our own client edge border when themed
  3996. //
  3997. if (ped->hTheme && TESTFLAG(GET_EXSTYLE(ped), WS_EX_CLIENTEDGE))
  3998. {
  3999. if (Edit_ClientEdgePaint(ped, ((wParam != 1) ? (HRGN)wParam : NULL)))
  4000. {
  4001. break;
  4002. }
  4003. }
  4004. goto HandleEditMsg;
  4005. case WM_WININICHANGE:
  4006. InitGlobalMetrics(wParam);
  4007. break;
  4008. case WM_IME_SETCONTEXT:
  4009. //
  4010. // If ped->fInsertCompChr is TRUE, that means we will do
  4011. // all the composition character drawing by ourself.
  4012. //
  4013. if (ped->fInsertCompChr )
  4014. {
  4015. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  4016. }
  4017. if (wParam)
  4018. {
  4019. PINPUTCONTEXT pInputContext;
  4020. HIMC hImc;
  4021. hImc = ImmGetContext(hwnd);
  4022. pInputContext = ImmLockIMC(hImc);
  4023. if (pInputContext != NULL)
  4024. {
  4025. pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS;
  4026. ImmUnlockIMC( hImc );
  4027. }
  4028. #if 0 // PORTPORT: Expose GetClientInfo()
  4029. if (GetClientInfo()->CI_flags & CI_16BIT)
  4030. {
  4031. ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L);
  4032. }
  4033. #endif
  4034. ImmReleaseContext( hwnd, hImc );
  4035. }
  4036. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  4037. break;
  4038. case WM_IME_ENDCOMPOSITION:
  4039. Edit_InOutReconversionMode(ped, FALSE);
  4040. if (ped->fReplaceCompChr)
  4041. {
  4042. ICH ich;
  4043. HDC hdc;
  4044. //
  4045. // we have a DBCS character to be replaced.
  4046. // let's delete it before inserting the new one.
  4047. //
  4048. ich = (ped->fAnsi) ? 2 : 1;
  4049. ped->fReplaceCompChr = FALSE;
  4050. ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch);
  4051. ped->ichMinSel = ped->ichCaret;
  4052. if (ped->fSingle)
  4053. {
  4054. if (Edit_DeleteText( ped ) > 0)
  4055. {
  4056. //
  4057. // Update the display
  4058. //
  4059. Edit_NotifyParent(ped, EN_UPDATE);
  4060. hdc = Edit_GetDC(ped, FALSE);
  4061. EditSL_DrawText(ped, hdc, 0);
  4062. Edit_ReleaseDC(ped, hdc, FALSE);
  4063. //
  4064. // Tell parent our text contents changed.
  4065. //
  4066. Edit_NotifyParent(ped, EN_CHANGE);
  4067. }
  4068. }
  4069. else
  4070. {
  4071. EditML_DeleteText(ped);
  4072. }
  4073. Edit_SetCaretHandler( ped );
  4074. }
  4075. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  4076. break;
  4077. case WM_IME_STARTCOMPOSITION:
  4078. if ( ped->fInsertCompChr )
  4079. {
  4080. //
  4081. // BUG BUG
  4082. //
  4083. // sending WM_IME_xxxCOMPOSITION will let
  4084. // IME draw composition window. IME should
  4085. // not do that since we cleared
  4086. // ISC_SHOWUICOMPOSITIONWINDOW bit when
  4087. // we got WM_IME_SETCONTEXT message.
  4088. //
  4089. // Korean IME should be fixed in the future.
  4090. //
  4091. break;
  4092. }
  4093. else
  4094. {
  4095. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  4096. }
  4097. break;
  4098. case WM_IME_COMPOSITION:
  4099. //
  4100. // simple composition character support for FE IME.
  4101. //
  4102. lResult = Edit_ImeComposition(ped, wParam, lParam);
  4103. break;
  4104. case WM_IME_NOTIFY:
  4105. if (ped->fInReconversion && (wParam == IMN_GUIDELINE))
  4106. {
  4107. HIMC hImc = ImmGetContext(hwnd);
  4108. if ((hImc != NULL_HIMC) && (ImmGetGuideLine(hImc, GGL_LEVEL, NULL, 0) >= GL_LEVEL_WARNING))
  4109. {
  4110. // #266916 Restore the cursor if conversion failed. Conversion can fail
  4111. // if you try converting 100+ chars at once.
  4112. Edit_InOutReconversionMode(ped, FALSE);
  4113. }
  4114. }
  4115. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  4116. break;
  4117. case WM_KILLFOCUS:
  4118. //
  4119. // remove any tips
  4120. //
  4121. if (ped->hwndBalloon)
  4122. {
  4123. BOOL fClickedTip = (ped->hwndBalloon == (HWND)wParam) ? TRUE : FALSE;
  4124. Edit_HideBalloonTip(ped->hwnd);
  4125. if (fClickedTip)
  4126. {
  4127. //
  4128. // Don't remove focus from the edit because they
  4129. // clicked on the tip.
  4130. //
  4131. SetFocus(hwnd);
  4132. break;
  4133. }
  4134. }
  4135. //
  4136. // when focus is removed from the window,
  4137. // composition character should be finalized
  4138. //
  4139. if (ped && g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0)))
  4140. {
  4141. HIMC hImc = ImmGetContext(hwnd);
  4142. if (hImc != NULL_HIMC)
  4143. {
  4144. if (ped->fReplaceCompChr || (ped->wImeStatus & EIMES_COMPLETECOMPSTRKILLFOCUS))
  4145. {
  4146. //
  4147. // If the composition string to be determined upon kill focus,
  4148. // do it now.
  4149. //
  4150. ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  4151. }
  4152. else if (ped->fInReconversion)
  4153. {
  4154. //
  4155. // If the composition string it not to be determined,
  4156. // and if we're in reconversion mode, cancel reconversion now.
  4157. //
  4158. ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  4159. }
  4160. //
  4161. // Get out from reconversion mode
  4162. //
  4163. if (ped->fInReconversion)
  4164. {
  4165. Edit_InOutReconversionMode(ped, FALSE);
  4166. }
  4167. ImmReleaseContext(hwnd, hImc);
  4168. }
  4169. }
  4170. goto HandleEditMsg;
  4171. break;
  4172. case WM_SETFOCUS:
  4173. if (ped && !ped->fFocus)
  4174. {
  4175. HKL hkl = GetKeyboardLayout(0);
  4176. if (g_fIMMEnabled && ImmIsIME(hkl))
  4177. {
  4178. HIMC hImc;
  4179. hImc = ImmGetContext(hwnd);
  4180. if (hImc)
  4181. {
  4182. LPINPUTCONTEXT lpImc;
  4183. if (ped->wImeStatus & EIMES_CANCELCOMPSTRINFOCUS)
  4184. {
  4185. //
  4186. // cancel when in-focus
  4187. //
  4188. ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  4189. }
  4190. Edit_SetCompositionFont(ped);
  4191. if ((lpImc = ImmLockIMC(hImc)) != NULL)
  4192. {
  4193. //
  4194. // We presume the CompForm will reset to CFS_DEFAULT,
  4195. // when the edit control loses Focus.
  4196. // IMEWndProc32 will call ImmSetCompositionWindow with
  4197. // CFS_DEFAULT, when it receive WM_IME_SETCONTEXT.
  4198. //
  4199. lpImc->fdw31Compat |= F31COMPAT_ECSETCFS;
  4200. ImmUnlockIMC(hImc);
  4201. }
  4202. ImmReleaseContext(hwnd, hImc);
  4203. }
  4204. //
  4205. // force to set IME composition window when
  4206. // first getting focus.
  4207. //
  4208. ped->ptScreenBounding.x = -1;
  4209. ped->ptScreenBounding.y = -1;
  4210. }
  4211. Edit_InitInsert(ped, hkl);
  4212. }
  4213. goto HandleEditMsg;
  4214. break;
  4215. case WM_IME_REQUEST:
  4216. //
  4217. // simple ImeRequest Handler
  4218. //
  4219. lResult = Edit_RequestHandler(ped, wParam, lParam);
  4220. break;
  4221. case WM_CREATE:
  4222. if (g_fIMMEnabled && ped)
  4223. {
  4224. Edit_EnableDisableIME(ped);
  4225. }
  4226. goto HandleEditMsg;
  4227. break;
  4228. case WM_GETOBJECT:
  4229. if(lParam == OBJID_QUERYCLASSNAMEIDX)
  4230. {
  4231. lResult = MSAA_CLASSNAMEIDX_EDIT;
  4232. }
  4233. else
  4234. {
  4235. lResult = FALSE;
  4236. }
  4237. break;
  4238. case WM_THEMECHANGED:
  4239. if ( ped->hTheme )
  4240. {
  4241. CloseThemeData(ped->hTheme);
  4242. }
  4243. ped->hTheme = OpenThemeData(ped->hwnd, L"Edit");
  4244. if ( ped->hFontSave )
  4245. {
  4246. Edit_SetFont(ped, ped->hFontSave, FALSE);
  4247. }
  4248. InvalidateRect(ped->hwnd, NULL, TRUE);
  4249. lResult = TRUE;
  4250. break;
  4251. case EM_SHOWBALLOONTIP:
  4252. lResult = Edit_ShowBalloonTipHandler(ped, (PEDITBALLOONTIP) lParam);
  4253. break;
  4254. case EM_HIDEBALLOONTIP:
  4255. lResult = Edit_HideBalloonTipHandler(ped);
  4256. break;
  4257. case WM_TIMER:
  4258. if (wParam == ID_EDITTIMER)
  4259. {
  4260. KillTimer(ped->hwnd, ID_EDITTIMER);
  4261. lResult = Edit_HideBalloonTip(ped->hwnd);
  4262. }
  4263. break;
  4264. default:
  4265. HandleEditMsg:
  4266. if (ped != NULL)
  4267. {
  4268. if (ped->fSingle)
  4269. {
  4270. lResult = EditSL_WndProc(ped, uMsg, wParam, lParam);
  4271. }
  4272. else
  4273. {
  4274. lResult = EditML_WndProc(ped, uMsg, wParam, lParam);
  4275. }
  4276. }
  4277. }
  4278. return lResult;
  4279. }
  4280. //---------------------------------------------------------------------------//
  4281. //
  4282. // Edit_FindXORblks
  4283. //
  4284. // This finds the XOR of lpOldBlk and lpNewBlk and return s resulting blocks
  4285. // through the lpBlk1 and lpBlk2; This could result in a single block or
  4286. // at the maximum two blocks;
  4287. // If a resulting block is empty, then it's StPos field has -1.
  4288. //
  4289. // NOTE:
  4290. // When called from MultiLine edit control, StPos and EndPos fields of
  4291. // these blocks have the Starting line and Ending line of the block;
  4292. // When called from SingleLine edit control, StPos and EndPos fields
  4293. // of these blocks have the character index of starting position and
  4294. // ending position of the block.
  4295. //
  4296. VOID Edit_FindXORblks(LPSELBLOCK lpOldBlk, LPSELBLOCK lpNewBlk, LPSELBLOCK lpBlk1, LPSELBLOCK lpBlk2)
  4297. {
  4298. if (lpOldBlk->StPos >= lpNewBlk->StPos)
  4299. {
  4300. lpBlk1->StPos = lpNewBlk->StPos;
  4301. lpBlk1->EndPos = min(lpOldBlk->StPos, lpNewBlk->EndPos);
  4302. }
  4303. else
  4304. {
  4305. lpBlk1->StPos = lpOldBlk->StPos;
  4306. lpBlk1->EndPos = min(lpNewBlk->StPos, lpOldBlk->EndPos);
  4307. }
  4308. if (lpOldBlk->EndPos <= lpNewBlk->EndPos)
  4309. {
  4310. lpBlk2->StPos = max(lpOldBlk->EndPos, lpNewBlk->StPos);
  4311. lpBlk2->EndPos = lpNewBlk->EndPos;
  4312. }
  4313. else
  4314. {
  4315. lpBlk2->StPos = max(lpNewBlk->EndPos, lpOldBlk->StPos);
  4316. lpBlk2->EndPos = lpOldBlk->EndPos;
  4317. }
  4318. }
  4319. //---------------------------------------------------------------------------//
  4320. //
  4321. BOOL Edit_CalcChangeSelection(PED ped, ICH ichOldMinSel, ICH ichOldMaxSel, LPSELBLOCK OldBlk, LPSELBLOCK NewBlk)
  4322. {
  4323. SELBLOCK Blk[2];
  4324. int iBlkCount = 0;
  4325. Blk[0].StPos = Blk[0].EndPos = Blk[1].StPos = Blk[1].EndPos = 0xFFFFFFFF;
  4326. //
  4327. // Check if the Old selection block existed
  4328. //
  4329. if (ichOldMinSel != ichOldMaxSel)
  4330. {
  4331. //
  4332. // Yes! Old block existed.
  4333. //
  4334. Blk[0].StPos = OldBlk->StPos;
  4335. Blk[0].EndPos = OldBlk->EndPos;
  4336. iBlkCount++;
  4337. }
  4338. //
  4339. // Check if the new Selection block exists
  4340. //
  4341. if (ped->ichMinSel != ped->ichMaxSel)
  4342. {
  4343. //
  4344. // Yes! New block exists
  4345. //
  4346. Blk[1].StPos = NewBlk->StPos;
  4347. Blk[1].EndPos = NewBlk->EndPos;
  4348. iBlkCount++;
  4349. }
  4350. //
  4351. // If both the blocks exist find the XOR of them
  4352. //
  4353. if (iBlkCount == 2)
  4354. {
  4355. //
  4356. // Check if both blocks start at the same character position
  4357. //
  4358. if (ichOldMinSel == ped->ichMinSel)
  4359. {
  4360. //
  4361. // Check if they end at the same character position
  4362. //
  4363. if (ichOldMaxSel == ped->ichMaxSel)
  4364. {
  4365. //
  4366. // Nothing changes
  4367. //
  4368. return FALSE;
  4369. }
  4370. Blk[0].StPos = min(NewBlk -> EndPos, OldBlk -> EndPos);
  4371. Blk[0].EndPos = max(NewBlk -> EndPos, OldBlk -> EndPos);
  4372. Blk[1].StPos = 0xFFFFFFFF;
  4373. }
  4374. else
  4375. {
  4376. if (ichOldMaxSel == ped->ichMaxSel)
  4377. {
  4378. Blk[0].StPos = min(NewBlk->StPos, OldBlk->StPos);
  4379. Blk[0].EndPos = max(NewBlk->StPos, OldBlk->StPos);
  4380. Blk[1].StPos = 0xFFFFFFFF;
  4381. }
  4382. else
  4383. {
  4384. Edit_FindXORblks(OldBlk, NewBlk, &Blk[0], &Blk[1]);
  4385. }
  4386. }
  4387. }
  4388. RtlCopyMemory(OldBlk, &Blk[0], sizeof(SELBLOCK));
  4389. RtlCopyMemory(NewBlk, &Blk[1], sizeof(SELBLOCK));
  4390. return TRUE;
  4391. }
  4392. //---------------------------------------------------------------------------//
  4393. //
  4394. // Edit_GetControlBrush
  4395. //
  4396. // Client side optimization replacement for NtUserGetControlBrush
  4397. // message is one of the WM_CTLCOLOR* messages.
  4398. //
  4399. HBRUSH Edit_GetControlBrush(PED ped, HDC hdc, LONG message)
  4400. {
  4401. HWND hwndSend;
  4402. hwndSend = (GET_STYLE(ped) & WS_POPUP) ? GetWindowOwner(ped->hwnd) : GetParent(ped->hwnd);
  4403. if (!hwndSend)
  4404. {
  4405. hwndSend = ped->hwnd;
  4406. }
  4407. //
  4408. // By using the correct A/W call we avoid a c/s transition
  4409. // on this SendMessage().
  4410. //
  4411. return (HBRUSH)SendMessage(hwndSend, message, (WPARAM)hdc, (LPARAM)ped->hwnd);
  4412. }
  4413. //---------------------------------------------------------------------------//
  4414. //
  4415. // Edit_GetDBCSVector
  4416. //
  4417. // This function sets DBCS Vector for specified character set and sets
  4418. // ped->fDBCS flag if needed.
  4419. //
  4420. INT Edit_GetDBCSVector(PED ped, HDC hdc, BYTE CharSet)
  4421. {
  4422. BOOL bDBCSCodePage = FALSE;
  4423. static UINT fFontAssocStatus = 0xffff;
  4424. //
  4425. // if DEFAUT_CHARSET was passed, we will convert that to Shell charset..
  4426. //
  4427. if (CharSet == DEFAULT_CHARSET)
  4428. {
  4429. CharSet = (BYTE)GetTextCharset(hdc);
  4430. //
  4431. // if CharSet is still DEFAULT_CHARSET, it means gdi has some problem..
  4432. // then just return default.. we get charset from CP_ACP..
  4433. //
  4434. if (CharSet == DEFAULT_CHARSET)
  4435. {
  4436. CharSet = (BYTE)GetACPCharSet();
  4437. }
  4438. }
  4439. switch (CharSet)
  4440. {
  4441. case SHIFTJIS_CHARSET:
  4442. case HANGEUL_CHARSET:
  4443. case CHINESEBIG5_CHARSET:
  4444. case GB2312_CHARSET:
  4445. bDBCSCodePage = TRUE;
  4446. break;
  4447. case ANSI_CHARSET: // 0
  4448. case SYMBOL_CHARSET: // 2
  4449. case OEM_CHARSET: // 255
  4450. if (fFontAssocStatus == 0xffff)
  4451. {
  4452. fFontAssocStatus = QueryFontAssocStatus();
  4453. }
  4454. if ((((CharSet + 2) & 0xf) & fFontAssocStatus))
  4455. {
  4456. bDBCSCodePage = TRUE;
  4457. //
  4458. // Bug 117558, etc.
  4459. // Try to get a meaningful character set for associated font.
  4460. //
  4461. CharSet = (BYTE)GetACPCharSet();
  4462. }
  4463. else
  4464. {
  4465. bDBCSCodePage = FALSE;
  4466. }
  4467. break;
  4468. default:
  4469. bDBCSCodePage = FALSE;
  4470. }
  4471. if (bDBCSCodePage)
  4472. {
  4473. CHARSETINFO CharsetInfo;
  4474. DWORD CodePage;
  4475. CPINFO CPInfo;
  4476. INT lbIX;
  4477. if (TranslateCharsetInfo((DWORD *)CharSet, &CharsetInfo, TCI_SRCCHARSET))
  4478. {
  4479. CodePage = CharsetInfo.ciACP;
  4480. }
  4481. else
  4482. {
  4483. CodePage = CP_ACP;
  4484. }
  4485. GetCPInfo(CodePage, &CPInfo);
  4486. for (lbIX=0 ; CPInfo.LeadByte[lbIX] != 0 ; lbIX+=2)
  4487. {
  4488. ped->DBCSVector[lbIX ] = CPInfo.LeadByte[lbIX];
  4489. ped->DBCSVector[lbIX+1] = CPInfo.LeadByte[lbIX+1];
  4490. }
  4491. ped->DBCSVector[lbIX ] = 0x0;
  4492. ped->DBCSVector[lbIX+1] = 0x0;
  4493. }
  4494. else
  4495. {
  4496. ped->DBCSVector[0] = 0x0;
  4497. ped->DBCSVector[1] = 0x0;
  4498. }
  4499. //
  4500. // Final check: if the font supports DBCS glyphs
  4501. //
  4502. // If we've got a font with DBCS glyphs, let's mark PED so.
  4503. // But since the font's primary charset is the one other than FE,
  4504. // we can only support UNICODE Edit control.
  4505. //
  4506. // a) GDI performs A/W conversion for ANSI apps based on the primary
  4507. // character set in hDC, so it will break anyway.
  4508. // b) ANSI applications are only supported on their native system locales:
  4509. // GetACPCharSet() is expected to return a FE code page.
  4510. // c) ANSI Edit control requires DBCSVector, which cannot be
  4511. // initialized without a FE code page.
  4512. //
  4513. if (!ped->fAnsi)
  4514. {
  4515. FONTSIGNATURE fontSig;
  4516. GetTextCharsetInfo(hdc, &fontSig, 0);
  4517. if (fontSig.fsCsb[0] &FAREAST_CHARSET_BITS)
  4518. {
  4519. //
  4520. // Since this is UNICODE, we're not
  4521. //
  4522. bDBCSCodePage = TRUE;
  4523. }
  4524. }
  4525. return bDBCSCodePage;
  4526. }
  4527. //---------------------------------------------------------------------------//
  4528. //
  4529. // Edit_AnsiNext
  4530. //
  4531. // This function advances string pointer for Edit Control use only.
  4532. //
  4533. LPSTR Edit_AnsiNext(PED ped, LPSTR lpCurrent)
  4534. {
  4535. return lpCurrent+((Edit_IsDBCSLeadByte(ped,*lpCurrent)==TRUE) ? 2 : 1);
  4536. }
  4537. //---------------------------------------------------------------------------//
  4538. //
  4539. // Edit_AnsiPrev
  4540. //
  4541. // This function decrements string pointer for Edit Control use only.
  4542. //
  4543. LPSTR Edit_AnsiPrev(PED ped, LPSTR lpBase, LPSTR lpStr )
  4544. {
  4545. LPSTR lpCurrent = lpStr -1;
  4546. if (!ped->fDBCS)
  4547. {
  4548. //
  4549. // just return ( lpStr - 1 )
  4550. //
  4551. return lpCurrent;
  4552. }
  4553. if (lpBase >= lpCurrent)
  4554. {
  4555. return lpBase;
  4556. }
  4557. //
  4558. // this check makes things faster
  4559. //
  4560. if (Edit_IsDBCSLeadByte(ped, *lpCurrent))
  4561. {
  4562. return (lpCurrent - 1);
  4563. }
  4564. do
  4565. {
  4566. lpCurrent--;
  4567. if (!Edit_IsDBCSLeadByte(ped, *lpCurrent))
  4568. {
  4569. lpCurrent++;
  4570. break;
  4571. }
  4572. }
  4573. while(lpCurrent != lpBase);
  4574. return lpStr - (((lpStr - lpCurrent) & 1) ? 1 : 2);
  4575. }
  4576. //---------------------------------------------------------------------------//
  4577. //
  4578. // Edit_NextIch
  4579. //
  4580. // This function advances string pointer for Edit Control use only.
  4581. //
  4582. ICH Edit_NextIch( PED ped, LPSTR pStart, ICH ichCurrent )
  4583. {
  4584. if (!ped->fDBCS || !ped->fAnsi)
  4585. {
  4586. return (ichCurrent + 1);
  4587. }
  4588. else
  4589. {
  4590. ICH ichRet;
  4591. LPSTR pText;
  4592. if (pStart)
  4593. {
  4594. pText = pStart + ichCurrent;
  4595. }
  4596. else
  4597. {
  4598. pText = (LPSTR)Edit_Lock(ped) + ichCurrent;
  4599. }
  4600. ichRet = ichCurrent + ( Edit_IsDBCSLeadByte(ped, *pText) ? 2 : 1 );
  4601. if (!pStart)
  4602. {
  4603. Edit_Unlock(ped);
  4604. }
  4605. return ichRet;
  4606. }
  4607. }
  4608. //---------------------------------------------------------------------------//
  4609. //
  4610. // Edit_PrevIch
  4611. //
  4612. // This function decrements string pointer for Edit Control use only.
  4613. //
  4614. ICH Edit_PrevIch(PED ped, LPSTR pStart, ICH ichCurrent)
  4615. {
  4616. LPSTR lpCurrent;
  4617. LPSTR lpStr;
  4618. LPSTR lpBase;
  4619. if (!ped->fDBCS || !ped->fAnsi)
  4620. {
  4621. if (ichCurrent)
  4622. {
  4623. return (ichCurrent - 1);
  4624. }
  4625. else
  4626. {
  4627. return (ichCurrent);
  4628. }
  4629. }
  4630. if (ichCurrent <= 1)
  4631. {
  4632. return 0;
  4633. }
  4634. if (pStart)
  4635. {
  4636. lpBase = pStart;
  4637. }
  4638. else
  4639. {
  4640. lpBase = Edit_Lock(ped);
  4641. }
  4642. lpStr = lpBase + ichCurrent;
  4643. lpCurrent = lpStr - 1;
  4644. if (Edit_IsDBCSLeadByte(ped,*lpCurrent))
  4645. {
  4646. if (!pStart)
  4647. {
  4648. Edit_Unlock(ped);
  4649. }
  4650. return (ichCurrent - 2);
  4651. }
  4652. do
  4653. {
  4654. lpCurrent--;
  4655. if (!Edit_IsDBCSLeadByte(ped, *lpCurrent))
  4656. {
  4657. lpCurrent++;
  4658. break;
  4659. }
  4660. }
  4661. while(lpCurrent != lpBase);
  4662. if (!pStart)
  4663. {
  4664. Edit_Unlock(ped);
  4665. }
  4666. return (ichCurrent - (((lpStr - lpCurrent) & 1) ? 1 : 2));
  4667. }
  4668. //---------------------------------------------------------------------------//
  4669. //
  4670. // Edit_IsDBCSLeadByte
  4671. //
  4672. // IsDBCSLeadByte for Edit Control use only.
  4673. //
  4674. BOOL Edit_IsDBCSLeadByte(PED ped, BYTE cch)
  4675. {
  4676. INT i;
  4677. if (!ped->fDBCS || !ped->fAnsi)
  4678. {
  4679. return (FALSE);
  4680. }
  4681. for (i = 0; ped->DBCSVector[i]; i += 2)
  4682. {
  4683. if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch))
  4684. {
  4685. return (TRUE);
  4686. }
  4687. }
  4688. return (FALSE);
  4689. }
  4690. //---------------------------------------------------------------------------//
  4691. //
  4692. // DbcsCombine
  4693. //
  4694. // Assemble two WM_CHAR messages to single DBCS character.
  4695. // If program detects first byte of DBCS character in WM_CHAR message,
  4696. // it calls this function to obtain second WM_CHAR message from queue.
  4697. // finally this routine assembles first byte and second byte into single
  4698. // DBCS character.
  4699. //
  4700. WORD DbcsCombine(HWND hwnd, WORD ch)
  4701. {
  4702. MSG msg;
  4703. INT i = 10; // loop counter to avoid the infinite loop
  4704. while (!PeekMessageA(&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE))
  4705. {
  4706. if (--i == 0)
  4707. return 0;
  4708. Sleep(1);
  4709. }
  4710. return (WORD)ch | ((WORD)(msg.wParam) << 8);
  4711. }
  4712. //---------------------------------------------------------------------------//
  4713. //
  4714. // Edit_AdjustIch
  4715. //
  4716. // This function adjusts a current pointer correctly. If a current
  4717. // pointer is lying between DBCS first byte and second byte, this
  4718. // function adjusts a current pointer to a first byte of DBCS position
  4719. // by decrement once.
  4720. //
  4721. ICH Edit_AdjustIch( PED ped, LPSTR lpstr, ICH ch )
  4722. {
  4723. ICH newch = ch;
  4724. if (!ped->fAnsi || !ped->fDBCS || newch == 0)
  4725. {
  4726. return ch;
  4727. }
  4728. if (!Edit_IsDBCSLeadByte(ped,lpstr[--newch]))
  4729. {
  4730. //
  4731. // previous char is SBCS
  4732. //
  4733. return ch;
  4734. }
  4735. while (1)
  4736. {
  4737. if (!Edit_IsDBCSLeadByte(ped,lpstr[newch]))
  4738. {
  4739. newch++;
  4740. break;
  4741. }
  4742. if (newch)
  4743. {
  4744. newch--;
  4745. }
  4746. else
  4747. {
  4748. break;
  4749. }
  4750. }
  4751. return ((ch - newch) & 1) ? ch-1 : ch;
  4752. }
  4753. //---------------------------------------------------------------------------//
  4754. //
  4755. // Edit_AdjustIchNext
  4756. //
  4757. ICH Edit_AdjustIchNext(PED ped, LPSTR lpstr, ICH ch)
  4758. {
  4759. ICH ichNew = Edit_AdjustIch(ped,lpstr,ch);
  4760. LPSTR lpnew = lpstr + ichNew;
  4761. //
  4762. // if ch > ichNew then Edit_AdjustIch adjusted ich.
  4763. //
  4764. if (ch > ichNew)
  4765. {
  4766. lpnew = Edit_AnsiNext(ped, lpnew);
  4767. }
  4768. return (ICH)(lpnew-lpstr);
  4769. }
  4770. //---------------------------------------------------------------------------//
  4771. //
  4772. // Edit_UpdateFormat
  4773. //
  4774. // Computes ped->format and ped->fRtoLReading from dwStyle and dwExStyle.
  4775. // Refreshes the display if either are changed.
  4776. //
  4777. VOID Edit_UpdateFormat(PED ped, DWORD dwStyle, DWORD dwExStyle)
  4778. {
  4779. UINT fNewRtoLReading;
  4780. UINT uiNewFormat;
  4781. //
  4782. // Extract new format and reading order from style
  4783. //
  4784. fNewRtoLReading = dwExStyle & WS_EX_RTLREADING ? 1 : 0;
  4785. uiNewFormat = dwStyle & ES_FMTMASK;
  4786. //
  4787. // WS_EX_RIGHT is ignored unless dwStyle is ES_LEFT
  4788. //
  4789. if (uiNewFormat == ES_LEFT && dwExStyle & WS_EX_RIGHT)
  4790. {
  4791. uiNewFormat = ES_RIGHT;
  4792. }
  4793. //
  4794. // Internally ES_LEFT and ES_RIGHT are swapped for RtoLReading order
  4795. // (Think of them as ES_LEADING and ES_TRAILING)
  4796. //
  4797. if (fNewRtoLReading)
  4798. {
  4799. switch (uiNewFormat)
  4800. {
  4801. case ES_LEFT:
  4802. uiNewFormat = ES_RIGHT;
  4803. break;
  4804. case ES_RIGHT:
  4805. uiNewFormat = ES_LEFT;
  4806. break;
  4807. }
  4808. }
  4809. //
  4810. // Format change does not cause redisplay by itself
  4811. //
  4812. ped->format = uiNewFormat;
  4813. //
  4814. // Refresh display on change of reading order
  4815. //
  4816. if (fNewRtoLReading != ped->fRtoLReading)
  4817. {
  4818. ped->fRtoLReading = fNewRtoLReading;
  4819. if (ped->fWrap)
  4820. {
  4821. //
  4822. // Redo wordwrap
  4823. //
  4824. EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  4825. EditML_UpdateiCaretLine(ped);
  4826. }
  4827. else
  4828. {
  4829. //
  4830. // Refresh horizontal scrollbar display
  4831. //
  4832. EditML_Scroll(ped, FALSE, 0xffffffff, 0, TRUE);
  4833. }
  4834. Edit_InvalidateClient(ped, TRUE);
  4835. }
  4836. }
  4837. //---------------------------------------------------------------------------//
  4838. //
  4839. // Edit_IsFullWidth
  4840. //
  4841. // Detects Far East FullWidth character.
  4842. //
  4843. BOOL Edit_IsFullWidth(DWORD dwCodePage,WCHAR wChar)
  4844. {
  4845. INT index;
  4846. INT cChars;
  4847. static struct _FULLWIDTH_UNICODE
  4848. {
  4849. WCHAR Start;
  4850. WCHAR End;
  4851. } FullWidthUnicodes[] =
  4852. {
  4853. { 0x4E00, 0x9FFF }, // CJK_UNIFIED_IDOGRAPHS
  4854. { 0x3040, 0x309F }, // HIRAGANA
  4855. { 0x30A0, 0x30FF }, // KATAKANA
  4856. { 0xAC00, 0xD7A3 } // HANGUL
  4857. };
  4858. //
  4859. // Early out for ASCII.
  4860. //
  4861. if (wChar < 0x0080)
  4862. {
  4863. //
  4864. // if the character < 0x0080, it should be a halfwidth character.
  4865. //
  4866. return FALSE;
  4867. }
  4868. //
  4869. // Scan FullWdith definition table... most of FullWidth character is
  4870. // defined here... this is more faster than call NLS API.
  4871. //
  4872. for (index = 0; index < ARRAYSIZE(FullWidthUnicodes); index++)
  4873. {
  4874. if ((wChar >= FullWidthUnicodes[index].Start) &&
  4875. (wChar <= FullWidthUnicodes[index].End))
  4876. {
  4877. return TRUE;
  4878. }
  4879. }
  4880. //
  4881. // if this Unicode character is mapped to Double-Byte character,
  4882. // this is also FullWidth character..
  4883. //
  4884. cChars = WideCharToMultiByte((UINT)dwCodePage, 0, &wChar, 1, NULL, 0, NULL, NULL);
  4885. return cChars > 1 ? TRUE : FALSE;
  4886. }