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

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