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.

2019 lines
51 KiB

  1. //// LPK_EDIT - Edit control support - C interface
  2. //
  3. // Handles all callouts from the standard US edit control.
  4. //
  5. // David C Brown (dbrown) 17th Nov 1996.
  6. //
  7. // Copyright (c) 1996-1997 Microsoft Corporation. All right reserved.
  8. /*
  9. * Core NT headers
  10. */
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <ntcsrdll.h>
  15. #include <ntcsrsrv.h>
  16. #define NONTOSPINTERLOCK
  17. #include <ntosp.h>
  18. /*
  19. * Standard C runtime headers
  20. */
  21. #include <limits.h>
  22. #include <memory.h>
  23. #include <stddef.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. /*
  27. * NtUser Client specific headers
  28. */
  29. #include "usercli.h"
  30. #include <winnlsp.h>
  31. #include <ntsdexts.h>
  32. #include <windowsx.h>
  33. #include <newres.h>
  34. #include <asdf.h>
  35. /*
  36. * Complex script language pack
  37. */
  38. #include "lpk.h"
  39. #include "lpk_glob.h"
  40. // Don't link directly to NtUserCreateCaret
  41. #undef CreateCaret
  42. /// Unicode control characters
  43. //
  44. #define U_TAB 0x0009
  45. #define U_FS 0x001C
  46. #define U_GS 0x001D
  47. #define U_RS 0x001E
  48. #define U_US 0x001F
  49. #define U_ZWNJ 0x200C
  50. #define U_ZWJ 0x200D
  51. #define U_LRM 0x200E
  52. #define U_RLM 0x200F
  53. #define U_LRE 0x202A
  54. #define U_RLE 0x202B
  55. #define U_PDF 0x202C
  56. #define U_LRO 0x202D
  57. #define U_RLO 0x202E
  58. #define U_ISS 0x206A
  59. #define U_ASS 0x206B
  60. #define U_IAFS 0x206C
  61. #define U_AAFS 0x206D
  62. #define U_NADS 0x206E
  63. #define U_NODS 0x206F
  64. #define TRACE(a,b)
  65. #define ASSERTS(a,b)
  66. #define ASSERTHR(a,b)
  67. /***************************************************************************\
  68. * BOOL ECIsDBCSLeadByte( PED ped, BYTE cch )
  69. *
  70. * IsDBCSLeadByte for Edit Control use only.
  71. *
  72. * History: 18-Jun-1996 Hideyuki Nagase
  73. \***************************************************************************/
  74. BOOL ECIsDBCSLeadByte(PED ped, BYTE cch)
  75. {
  76. int i;
  77. if (!ped->fDBCS || !ped->fAnsi)
  78. return (FALSE);
  79. for (i = 0; ped->DBCSVector[i]; i += 2) {
  80. if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch))
  81. return (TRUE);
  82. }
  83. return (FALSE);
  84. }
  85. //// GetEditAnsiConversionCharset - Figure a proper charset to MBTWC ANSI edit control's data
  86. //
  87. // In some Far East settings, they associate the symbol font to their ANSI codepage for
  88. // backward compatibility (this is not the case for Japanese though), otherwise we convert
  89. // using page 0. Currently Uniscribe glyph table maps SYMBOLIC_FONT to 3 pages - U+00xx,
  90. // U+F0xx and the system's ACP page.
  91. //
  92. // For Unicode control returns -1
  93. int GetEditAnsiConversionCharset(PED ped)
  94. {
  95. int iCharset = ped->fAnsi ? ped->charSet : -1;
  96. if (iCharset == SYMBOL_CHARSET || iCharset == OEM_CHARSET)
  97. {
  98. iCharset = ANSI_CHARSET; // assume page U+00xx
  99. }
  100. if (iCharset == ANSI_CHARSET && ped->fDBCS)
  101. {
  102. // In Chinese system, there is font association to map symbol to ACP
  103. // (QueryFontAssocStatus returns non-null). More detail please refer
  104. // to USER's ECGetDBCSVector(...)
  105. CHARSETINFO csi;
  106. if (TranslateCharsetInfo((DWORD*)UIntToPtr(g_ACP), &csi, TCI_SRCCODEPAGE))
  107. iCharset = csi.ciCharset;
  108. }
  109. return iCharset;
  110. }
  111. //// MBCPtoWCCP - Translate multi-byte caret position to wide char caret position
  112. //
  113. // Translates from a multibyte caret position specified as a byte offset
  114. // into an 8 bit string, to a widechar caret position, returned as a
  115. // word offset into a 16 bit character string.
  116. //
  117. // If the codepage isn't a DBCS, the imput offset is returned unchanged.
  118. //
  119. // Returns E_FAIL if icpMbStr addresses the second byte of a double byte character
  120. HRESULT MBCPtoWCCP(
  121. PED ped, // In - Edit control structure
  122. BYTE *pbMbStr, // In - Multi byte string
  123. int icpMbStr, // In - Byte offset of caret in multibyte string
  124. int *picpWcStr) { // Out - Wide char caret position
  125. if (!ped->fDBCS || !ped->fAnsi) {
  126. *picpWcStr = icpMbStr;
  127. return S_OK;
  128. }
  129. // Scan through DBCS string counting characters
  130. *picpWcStr = 0;
  131. while (icpMbStr > 0) {
  132. if (ECIsDBCSLeadByte(ped, *pbMbStr)) {
  133. // Character takes two bytes
  134. icpMbStr -= 2;
  135. pbMbStr += 2;
  136. } else {
  137. // Character takes one byte
  138. icpMbStr--;
  139. pbMbStr++;
  140. }
  141. (*picpWcStr)++;
  142. }
  143. return icpMbStr == 0 ? S_OK : E_FAIL;
  144. }
  145. //// WCCPtoMBCP - Translate wide char caret position to multi-byte caret position
  146. //
  147. // Translates from a widechar caret position specified as a word offset
  148. // into a 16 bit string, to a multibyte caret position, returned as a
  149. // byte offset into an 8 bit character string.
  150. HRESULT WCCPtoMBCP(
  151. PED ped, // In - Edit control structure
  152. BYTE *pbMbStr, // In - Multi byte string
  153. int icpWcStr, // In - Wide char caret position
  154. int *picpMbStr) { // Out - Byte offset of caret in multibyte string
  155. if (!ped->fDBCS || !ped->fAnsi) {
  156. *picpMbStr = icpWcStr;
  157. return S_OK;
  158. }
  159. // Scan through DBCS string counting characters
  160. *picpMbStr = 0;
  161. while (icpWcStr > 0) {
  162. if (ECIsDBCSLeadByte(ped, *pbMbStr)) {
  163. // Character takes two bytes
  164. (*picpMbStr) += 2;
  165. pbMbStr += 2;
  166. } else {
  167. // Character takes one byte
  168. (*picpMbStr)++;
  169. pbMbStr++;
  170. }
  171. icpWcStr--;
  172. }
  173. return S_OK;
  174. }
  175. //// LeftEdgeX
  176. //
  177. // Returns the visual x offset (i.e. from the left edge of the window)
  178. // to the left edge of a line of width iWidth given the current
  179. // formatting state of the edit control, .format and .xOffset.
  180. int LeftEdgeX(PED ped, INT iWidth) {
  181. INT iX;
  182. // First generate logical iX - offset forward from leading margin.
  183. iX = 0;
  184. switch (ped->format) {
  185. case ES_LEFT: // leading margin alignment
  186. if (ped->fWrap) {
  187. iX = 0;
  188. } else {
  189. iX = -(INT)ped->xOffset;
  190. }
  191. break;
  192. case ES_CENTER:
  193. iX = (ped->rcFmt.right - ped->rcFmt.left - iWidth) / 2;
  194. break;
  195. case ES_RIGHT: // far margin alignment
  196. iX = ped->rcFmt.right - ped->rcFmt.left - iWidth;
  197. break;
  198. }
  199. // iX is logical offset from leading margin to leading edge of string
  200. if (ped->format != ES_LEFT && iX < 0) {
  201. iX = !ped->fWrap ? -(INT)ped->xOffset : 0;
  202. }
  203. // Now adjust for right to left origin and incorporate left margin
  204. if (ped->fRtoLReading) {
  205. iX = ped->rcFmt.right - (iX+iWidth);
  206. } else {
  207. iX += ped->rcFmt.left;
  208. }
  209. TRACE(EDIT, ("LeftEdgeX iWidth=%d, format=%d, xOffset=%d, fWrap=%d, fRtoLReading=%d, right-left=%d, returning %d",
  210. iWidth, ped->format, ped->xOffset, ped->fWrap, ped->fRtoLReading, ped->rcFmt.right - ped->rcFmt.left, iX));
  211. return iX;
  212. }
  213. ///// Shaping engins IDs.
  214. #define BIDI_SHAPING_ENGINE_DLL 1<<0
  215. #define THAI_SHAPING_ENGINE_DLL 1<<1
  216. #define INDIAN_SHAPING_ENGINE_DLL 1<<4
  217. ///
  218. //// EditCreate
  219. //
  220. // Called from edecrare.c ECCreate.
  221. //
  222. // Return TRUE if create succeeded
  223. BOOL EditCreate(PED ped, HWND hWnd) {
  224. LONG_PTR dwExStyle, dwStyle;
  225. TRACE(EDIT, ("EditCreate called."));
  226. // Check if BIDI shaping engine is loaded then
  227. // allow the edit control to switch its direction.
  228. if (g_dwLoadedShapingDLLs & BIDI_SHAPING_ENGINE_DLL) {
  229. ped->fAllowRTL = TRUE;
  230. } else {
  231. ped->fAllowRTL = FALSE;
  232. }
  233. // Process WS_EX flags
  234. dwExStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
  235. if (dwExStyle & WS_EX_LAYOUTRTL) {
  236. dwExStyle = dwExStyle & ~WS_EX_LAYOUTRTL;
  237. dwExStyle = dwExStyle ^ (WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
  238. SetWindowLongPtr(hWnd, GWL_EXSTYLE, dwExStyle);
  239. dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
  240. if (!(dwStyle & ES_CENTER)) {
  241. dwStyle = dwStyle ^ ES_RIGHT;
  242. SetWindowLongPtr(hWnd, GWL_STYLE, dwStyle);
  243. }
  244. }
  245. if (dwExStyle & WS_EX_RIGHT && ped->format == ES_LEFT) {
  246. ped->format = ES_RIGHT;
  247. }
  248. if (dwExStyle & WS_EX_RTLREADING) {
  249. ped->fRtoLReading = TRUE;
  250. switch (ped->format) {
  251. case ES_LEFT: ped->format = ES_RIGHT; break;
  252. case ES_RIGHT: ped->format = ES_LEFT; break;
  253. }
  254. }
  255. return TRUE;
  256. }
  257. //// EditStringAnalyse
  258. //
  259. // Creates standard analysis parameters from the PED
  260. HRESULT EditStringAnalyse(
  261. HDC hdc,
  262. PED ped,
  263. PSTR pText,
  264. int cch,
  265. DWORD dwFlags,
  266. int iMaxExtent,
  267. STRING_ANALYSIS **ppsa){
  268. HRESULT hr;
  269. SCRIPT_TABDEF std;
  270. int iTabExtent;
  271. if (!ped->pTabStops)
  272. {
  273. std.cTabStops = 1;
  274. std.iScale = 4;
  275. std.pTabStops = &iTabExtent;
  276. std.iTabOrigin = 0;
  277. iTabExtent = ped->aveCharWidth * 8;
  278. }
  279. else
  280. {
  281. std.cTabStops = *ped->pTabStops;
  282. std.iScale = 4; // Tabstops are already in device units
  283. std.pTabStops = ped->pTabStops + 1;
  284. std.iTabOrigin = 0;
  285. }
  286. hr = LpkStringAnalyse(
  287. hdc,
  288. ped->charPasswordChar ? (char*)&ped->charPasswordChar : pText,
  289. cch, 0,
  290. GetEditAnsiConversionCharset(ped),
  291. dwFlags | SSA_FALLBACK | SSA_TAB
  292. | (ped->fRtoLReading ? SSA_RTL : 0)
  293. | (ped->fDisplayCtrl ? SSA_DZWG : 0)
  294. | (ped->charPasswordChar ? SSA_PASSWORD : 0),
  295. -1, iMaxExtent,
  296. NULL, NULL, // Control, State
  297. NULL, // Overriding Dx array
  298. &std, // Tab definition
  299. NULL, // Input class overrides
  300. ppsa);
  301. if (FAILED(hr)) {
  302. ASSERTHR(hr, ("EditStringAnalyse - LpkStringAnalyse"));
  303. }
  304. return hr;
  305. }
  306. //// HScroll
  307. //
  308. // Checks wether the cursor is visible withing the rcFormat
  309. // area, and if not, updates xOffset so that it's 1/4 of the
  310. // way from the edge it was closest to.
  311. //
  312. // However, it never leaves whitespace between the leading margin
  313. // and the leading edge of the string, nor will it leave whitespace
  314. // between the trailing edge and the trailing margin when there's
  315. // enough text in the string to fill the whole window.
  316. //
  317. // Implemented for the single line edit control.
  318. BOOL EditHScroll(PED ped, HDC hdc, PSTR pText) {
  319. int ichCaret;
  320. int dx; // Distance to move RIGHTWARDS (i.e. visually)
  321. int cx; // Original visual cursor position
  322. int tw; // Text width
  323. int rw; // Rectangle width
  324. int ix; // ScriptCPtoX result
  325. UINT uOldXOffset;
  326. HRESULT hr;
  327. STRING_ANALYSIS *psa;
  328. if (!ped->cch || ped->ichCaret > ped->cch) {
  329. ped->xOffset = 0;
  330. return FALSE;
  331. }
  332. hr = EditStringAnalyse(hdc, ped, pText, ped->cch, SSA_GLYPHS, 0, &psa);
  333. if (FAILED(hr)) {
  334. ASSERTHR(hr, ("EditHScroll - EditStringAnalyse"));
  335. return FALSE;
  336. }
  337. MBCPtoWCCP(ped, pText, ped->ichCaret, &ichCaret);
  338. uOldXOffset = ped->xOffset;
  339. tw = psa->size.cx; // Text width
  340. rw = ped->rcFmt.right-ped->rcFmt.left; // Window rectangle width
  341. #ifdef CURSOR_ABSOLUTE_HOME_AND_END
  342. if (ichCaret <= 0) {
  343. cx = ped->fRtoLReading ? psa->size.cx : 0;
  344. } else if (ichCaret >= psa->cInChars) {
  345. cx = ped->fRtoLReading ? 0: psa->size.cx;
  346. } else {
  347. cx = ScriptCursorX(psa, ichCaret-1);
  348. }
  349. cx += LeftEdgeX(ped, tw);
  350. #else
  351. if (ichCaret <= 0) {
  352. hr = ScriptStringCPtoX(psa, ichCaret, FALSE, &ix);
  353. } else {
  354. hr = ScriptStringCPtoX(psa, ichCaret-1, TRUE, &ix);
  355. }
  356. if (FAILED(hr)) {
  357. ASSERTHR(hr, ("EditHScroll - ScriptStringCPtoX"));
  358. ScriptStringFree(&psa);
  359. return FALSE;
  360. }
  361. cx = LeftEdgeX(ped, tw) + ix;
  362. #endif
  363. if (cx < ped->rcFmt.left) {
  364. // Bring cursor position to left quartile
  365. dx = rw/4 - cx;
  366. } else if (cx > ped->rcFmt.right) {
  367. // Bring cursor position to right quartile
  368. dx = (3*rw)/4 - cx;
  369. } else
  370. dx = 0;
  371. // Adjust visual position change to logical - relative to reading order
  372. if (ped->fRtoLReading) {
  373. dx = - dx;
  374. }
  375. // Avoid unnecessary leading or trailing whitespace
  376. if (tw - ((INT)ped->xOffset - dx) < rw && tw > rw ) {
  377. // No need to have white space at the end if there's enough text
  378. ped->xOffset = (UINT)(tw-rw);
  379. } else if ((INT)ped->xOffset < dx) {
  380. // No need to have white space at beginning of line
  381. ped->xOffset = 0;
  382. } else {
  383. // Move cursor directly to chosen quartile
  384. ped->xOffset -= (UINT) dx;
  385. }
  386. TRACE(EDIT, ("HScroll format=%d, fWrap=%d, fRtoLReading=%d, right-left=%d, new xOffset %d",
  387. ped->format, ped->fWrap, ped->fRtoLReading, ped->rcFmt.right - ped->rcFmt.left, ped->xOffset));
  388. ScriptStringFree(&psa);
  389. return ped->xOffset != uOldXOffset ? TRUE : FALSE;
  390. }
  391. //// IchToXY
  392. //
  393. // Converts a character position to the corresponding x coordinate
  394. // offset from the left end of the text of the line.
  395. int EditIchToXY(PED ped, HDC hdc, PSTR pText, ICH ichLength, ICH ichPos) {
  396. INT iResult;
  397. HRESULT hr;
  398. STRING_ANALYSIS *psa;
  399. if (ichLength == 0) {
  400. return LeftEdgeX(ped, 0);
  401. }
  402. hr = EditStringAnalyse(hdc, ped, pText, ichLength, SSA_GLYPHS, 0, &psa);
  403. if (FAILED(hr)) {
  404. ASSERTHR(hr, ("EditIchToXY - EditStringAnalyse"));
  405. return LeftEdgeX(ped, 0);
  406. }
  407. MBCPtoWCCP(ped, pText, ichPos, &ichPos);
  408. #ifdef CURSOR_ABSOLUTE_HOME_AND_END
  409. if (ichPos <= 0) {
  410. iResult = ped->fRtoLReading ? psa->size.cx : 0;
  411. } else if (ichPos >= psa->cInChars) {
  412. iResult = ped->fRtoLReading ? 0 : psa->size.cx;
  413. } else {
  414. iResult = ScriptStringCPtoX(psa, ichPos-1);
  415. }
  416. #else
  417. if (ichPos <= 0) {
  418. hr = ScriptStringCPtoX(psa, ichPos, FALSE, &iResult);
  419. } else {
  420. hr = ScriptStringCPtoX(psa, ichPos-1, TRUE, &iResult);
  421. }
  422. if (FAILED(hr)) {
  423. ASSERTHR(hr, ("EditIchToXY - ScriptStringCPtoX"));
  424. iResult = 0;
  425. }
  426. #endif
  427. iResult += LeftEdgeX(ped, psa->size.cx);
  428. ScriptStringFree(&psa);
  429. return (int) iResult;
  430. }
  431. //// EditDrawText - draw one line for MLDrawText
  432. //
  433. // Draws the text at offset ichStart from pText length ichLength.
  434. //
  435. // entry pwText - points to beginning of line to display
  436. // iMinSel, - range of characters to be highlighted. May
  437. // iMaxSel be -ve or > cch.
  438. //
  439. // Uses ED structure fields as follows:
  440. //
  441. // rcFmt - drawing area
  442. // xOffset - distance from leading margin to leading edge of text.
  443. // (May be negative if the text is horizontally scrolled).
  444. // RtoLReading - determines leading margin/edge.
  445. // format - ES_LEFT - leading edge aligned (may be scrolled horizontally)
  446. // - ES_CENTRE - centred between margins. (can't be scrolled horizontally)
  447. // - ES_RIGHT - trailing margin aligned (can't be scrolled horizontally)
  448. void EditDrawText(PED ped, HDC hdc, PSTR pText, INT iLength, INT iMinSel, INT iMaxSel, INT iY) {
  449. INT iX; // x position to start display at
  450. INT iWidth; // Width of line
  451. RECT rc; // Locally updated copy of rectangle
  452. int xFarOffset;
  453. HRESULT hr;
  454. STRING_ANALYSIS *psa;
  455. // Establish where to display the line
  456. rc = ped->rcFmt;
  457. rc.top = iY;
  458. rc.bottom = iY + ped->lineHeight;
  459. xFarOffset = ped->xOffset + rc.right - rc.left;
  460. // Include left or right margins in display unless clipped
  461. // by horizontal scrolling.
  462. if (ped->wLeftMargin) {
  463. if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
  464. && ( (!ped->fRtoLReading && ped->xOffset > 0) // LTR and first char not fully in view
  465. || ( ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { //RTL and last char not fully in view
  466. rc.left -= ped->wLeftMargin;
  467. }
  468. }
  469. if (ped->wRightMargin) {
  470. if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
  471. && ( ( ped->fRtoLReading && ped->xOffset > 0) // RTL and first char not fully in view
  472. || (!ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { // LTR and last char not fully in view
  473. rc.right += ped->wRightMargin;
  474. }
  475. }
  476. if (iMinSel < 0) iMinSel = 0;
  477. if (iMaxSel > iLength) iMaxSel = iLength;
  478. if (ped->fSingle) {
  479. // The single line edit control always applies the background color
  480. SetBkMode(hdc, OPAQUE);
  481. }
  482. if (iLength <= 0) {
  483. if ((iMinSel < iMaxSel) || (GetBkMode(hdc) == OPAQUE)) {
  484. // Empty line, just clear it on screen
  485. ExtTextOutW(hdc, 0,iY, ETO_OPAQUE, &rc, NULL, 0, NULL);
  486. }
  487. return;
  488. }
  489. hr = EditStringAnalyse(hdc, ped, pText, iLength, SSA_GLYPHS, 0, &psa);
  490. if (FAILED(hr)) {
  491. ASSERTHR(hr, ("EditDrawText - EditStringAnalyse"));
  492. return;
  493. }
  494. MBCPtoWCCP(ped, pText, iMinSel, &iMinSel);
  495. MBCPtoWCCP(ped, pText, iMaxSel, &iMaxSel);
  496. iWidth = psa->size.cx;
  497. iX = LeftEdgeX(ped, iWidth); // Visual x where left edge of string should be.
  498. ScriptStringOut(
  499. psa,
  500. iX,
  501. iY,
  502. ETO_CLIPPED
  503. | (GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0),
  504. &rc,
  505. iMinSel,
  506. ped->fNoHideSel || ped->fFocus ? iMaxSel : iMinSel,
  507. ped->fDisabled);
  508. ScriptStringFree(&psa);
  509. }
  510. //// EditMouseToIch
  511. //
  512. // Returns the logical character offset corresponding to
  513. // a specified x offset.
  514. //
  515. // entry iX - Window (visual) x position
  516. ICH EditMouseToIch(PED ped, HDC hdc, PSTR pText, ICH ichCount, INT iX) {
  517. ICH iCh;
  518. BOOL fTrailing;
  519. int iWidth;
  520. HRESULT hr;
  521. STRING_ANALYSIS *psa;
  522. if (ichCount == 0) {
  523. return 0;
  524. }
  525. hr = EditStringAnalyse(hdc, ped, pText, ichCount, SSA_GLYPHS, 0, &psa);
  526. if (FAILED(hr)) {
  527. ASSERTHR(hr, ("EditMouseToIch - EditStringAnalyse"));
  528. return 0;
  529. }
  530. iWidth = psa->size.cx;
  531. // Take horizontal scroll position into consideration.
  532. iX -= LeftEdgeX(ped, iWidth);
  533. // If the user clicked beyond the edge of the string, treat it as a logical
  534. // start or end of string request.
  535. if (iX < 0) {
  536. iCh = ped->fRtoLReading ? ichCount : 0;
  537. } else if (iX > iWidth) {
  538. TRACE(POSN, ("LpkEditMouseToIch iX beyond right edge: iX %d, psa->piOutVW %x, psa->nOutGlyphs %d, psa->piDx[psa->nOutGlyphs-1] %d",
  539. iX, psa->piOutVW, psa->nOutGlyphs, iWidth));
  540. iCh = ped->fRtoLReading ? 0 : ichCount;
  541. } else {
  542. // Otherwise it's in the string. Find the logical character whose centre is nearest.
  543. ScriptStringXtoCP(psa, iX, &iCh, &fTrailing);
  544. iCh += fTrailing; // Snap to nearest character edge
  545. WCCPtoMBCP(ped, pText, iCh, &iCh);
  546. }
  547. ScriptStringFree(&psa);
  548. TRACE(POSN, ("EditMouseToIch iX %d returns ch %d", iX, iCh));
  549. return iCh;
  550. }
  551. //// EditGetLineWidth
  552. //
  553. // Returns width of line in pixels
  554. INT EditGetLineWidth(PED ped, HDC hdc, PSTR pText, ICH cch) {
  555. INT iResult;
  556. HRESULT hr;
  557. STRING_ANALYSIS *psa;
  558. if (cch == 0) {
  559. return 0;
  560. }
  561. if (cch > MAXLINELENGTH) {
  562. cch = MAXLINELENGTH;
  563. }
  564. hr = EditStringAnalyse(hdc, ped, pText, cch, SSA_GLYPHS, 0, &psa);
  565. if (FAILED(hr)) {
  566. ASSERTHR(hr, ("EditGetLineWidth - EditStringAnalyse"));
  567. return 0;
  568. }
  569. iResult = psa->size.cx;
  570. ScriptStringFree(&psa);
  571. TRACE(EDIT, ("EditGetLineWidth width %d returns %d", cch, iResult))
  572. return iResult;
  573. }
  574. //// EditCchInWidth
  575. //
  576. // Returns number of characters that will fit in width pixels.
  577. ICH EditCchInWidth(PED ped, HDC hdc, PSTR pText, ICH cch, int width) {
  578. ICH ichResult;
  579. HRESULT hr;
  580. STRING_ANALYSIS *psa;
  581. if (cch > MAXLINELENGTH) {
  582. cch = MAXLINELENGTH;
  583. } else if (cch == 0) {
  584. return 0;
  585. }
  586. hr = EditStringAnalyse(hdc, ped, pText, cch, SSA_GLYPHS | SSA_CLIP, width, &psa);
  587. if (FAILED(hr)) {
  588. ASSERTHR(hr, ("EditCchInWidth - EditStringAnalyse"));
  589. return 0;
  590. }
  591. ichResult = psa->cOutChars;
  592. WCCPtoMBCP(ped, pText, ichResult, &ichResult);
  593. ScriptStringFree(&psa);
  594. TRACE(EDIT, ("EditCchInWidth width %d returns %d", width, ichResult))
  595. return ichResult;
  596. }
  597. //// EditMoveSelection
  598. //
  599. // Returns nearest character position backward or forward from current position.
  600. //
  601. // Position is restricted according to language rules. For example, in Thai it is
  602. // not possible to position the cursor between a base consonant and it's
  603. // associated vowel or tone mark.
  604. ICH EditMoveSelection(PED ped, HDC hdc, PSTR pText, ICH ich, BOOL fBackward) {
  605. #define SP 0x20
  606. #define TAB 0x09
  607. #define CR 0x0D
  608. #define LF 0x0A
  609. #define EDWCH(ich) (ped->fAnsi ? (WCHAR)pText[ich] : ((PWSTR)pText)[ich])
  610. #define EDWCBLANK(ich) ((BOOL) (EDWCH(ich) == SP || EDWCH(ich) == TAB))
  611. #define EDWCCR(ich) ((BOOL) (EDWCH(ich) == CR))
  612. #define EDWCLF(ich) ((BOOL) (EDWCH(ich) == LF))
  613. #define EDSTARTWORD(ich) ( (ich == 0) \
  614. || ( ( EDWCBLANK(ich-1) \
  615. || EDWCLF(ich-1)) \
  616. && !EDWCBLANK(ich)) \
  617. || ( !EDWCCR(ich-1) \
  618. && EDWCCR(ich)))
  619. ICH ichNonblankStart; // Leading character of nonblank run containing potential caret position
  620. ICH ichNonblankLimit; // First character beyond nonblank run containing potential caret position
  621. int iOffset; // Offset into nonblank run of ich measued in logical characters
  622. STRING_ANALYSIS *psa;
  623. HRESULT hr;
  624. // Handle simple special cases:
  625. // o At very beginning or end of buffer
  626. // o When target position is blank or start or end of line
  627. if (fBackward) {
  628. if (ich <= 1) {
  629. return 0;
  630. }
  631. ich--;
  632. if (EDWCBLANK(ich)) {
  633. return ich;
  634. }
  635. if (EDWCLF(ich)) {
  636. while ( ich > 0
  637. && EDWCCR(ich-1)) {
  638. ich--;
  639. }
  640. return ich;
  641. }
  642. } else {
  643. if (ich >= ped->cch-1) {
  644. return ped->cch;
  645. }
  646. ich++;
  647. if (EDWCBLANK(ich)) {
  648. return ich;
  649. }
  650. if (EDWCCR(ich-1)) {
  651. // Moving forward from a CR.
  652. if ( ich < ped->cch
  653. && EDWCCR(ich)) {
  654. ich++;
  655. }
  656. if ( ich < ped->cch
  657. && EDWCLF(ich)) {
  658. ich++;
  659. }
  660. return ich;
  661. }
  662. }
  663. // Identify nonblank run containing target position
  664. ichNonblankStart = ich;
  665. ichNonblankLimit = ich+1;
  666. // Move ichNonblankStart back to real start of blank delimited run
  667. while ( ichNonblankStart > 0
  668. && !(EDSTARTWORD(ichNonblankStart))) {
  669. ichNonblankStart--;
  670. }
  671. // Include one leading space if any
  672. if ( ichNonblankStart > 0
  673. && EDWCBLANK(ichNonblankStart - 1)) {
  674. ichNonblankStart--;
  675. }
  676. // Move ichNonblankLimit on to real end of blank delimited run
  677. while ( ichNonblankLimit < ped->cch
  678. && !EDWCBLANK(ichNonblankLimit)
  679. && !EDWCCR(ichNonblankLimit)) {
  680. ichNonblankLimit++;
  681. }
  682. // Obtain a break analysis of the identified nonblank run
  683. hr = LpkStringAnalyse(
  684. hdc,
  685. pText + ichNonblankStart * ped->cbChar,
  686. ichNonblankLimit - ichNonblankStart,
  687. 0,
  688. GetEditAnsiConversionCharset(ped),
  689. SSA_BREAK,
  690. -1, 0,
  691. NULL, NULL, NULL, NULL, NULL,
  692. &psa);
  693. if (SUCCEEDED(hr)) {
  694. // Use the charstop flags in the logical attributes to correct ich
  695. if (ich <= ichNonblankStart) {
  696. iOffset = 0;
  697. } else {
  698. hr = MBCPtoWCCP(ped, pText+ichNonblankStart*ped->cbChar, ich-ichNonblankStart, &iOffset);
  699. if (hr == E_FAIL) {
  700. // ich was the second byte of a double byte character.
  701. // In this case MBCPtoWCCP has returned the subsequent character
  702. if (fBackward) {
  703. iOffset--;
  704. }
  705. }
  706. }
  707. if (fBackward) {
  708. while ( iOffset > 0
  709. && !psa->pLogAttr[iOffset].fCharStop) {
  710. iOffset--;
  711. }
  712. } else {
  713. while ( iOffset < psa->cInChars
  714. && !psa->pLogAttr[iOffset].fCharStop) {
  715. iOffset++;
  716. }
  717. }
  718. ScriptStringFree(&psa);
  719. WCCPtoMBCP(ped, pText+ichNonblankStart*ped->cbChar, iOffset, &ich);
  720. return ichNonblankStart + ich;
  721. } else {
  722. ASSERTHR(hr, ("EditMoveSelection - LpkStringAnalyse"));
  723. // Analysis not possible - ignore content of complex scripts.
  724. return ich;
  725. }
  726. }
  727. void EditGetNextBoundaries(
  728. PED ped,
  729. HDC hdc,
  730. PSTR pText,
  731. ICH ichStart,
  732. BOOL fLeft,
  733. ICH *pichMin,
  734. ICH *pichMax,
  735. BOOL fWordStop)
  736. {
  737. ICH sd,ed; // Start and end of blank delimited run
  738. ICH sc,ec; // Star and end of complex script word within sd,se
  739. HRESULT hr;
  740. STRING_ANALYSIS *psa;
  741. // Identify left end of nearest delimited word (see diagram above)
  742. sd = ichStart;
  743. if (fLeft) {
  744. // Going left
  745. if (sd) {
  746. sd--;
  747. while (!(EDSTARTWORD(sd))) {
  748. sd--;
  749. }
  750. }
  751. } else {
  752. // Going right
  753. if (EDWCBLANK(sd)) {
  754. // Move right to first character of word
  755. if (sd < ped->cch) {
  756. sd++;
  757. while (sd < ped->cch && !EDSTARTWORD(sd)) {
  758. sd++;
  759. }
  760. }
  761. } else {
  762. // Move left to first character of this word
  763. while (!EDSTARTWORD(sd)) {
  764. sd--;
  765. }
  766. }
  767. }
  768. // Position 'e' on first character of next word
  769. ed = sd;
  770. if (ed < ped->cch) {
  771. ed++;
  772. while (ed<ped->cch && !EDSTARTWORD(ed)) {
  773. ed++;
  774. }
  775. }
  776. // Obtain an analysis of the identified word
  777. hr = LpkStringAnalyse(
  778. hdc, pText + sd * ped->cbChar, ed - sd, 0,
  779. GetEditAnsiConversionCharset(ped),
  780. SSA_BREAK,
  781. -1, 0,
  782. NULL, NULL, NULL, NULL, NULL,
  783. &psa);
  784. if (SUCCEEDED(hr)) {
  785. // Use the start of word (linebreak) flags in the logical attribute
  786. // to narrow the word where appropriate to complex script handling
  787. if (ichStart > sd) {
  788. MBCPtoWCCP(ped, pText+sd*ped->cbChar, ichStart-sd, &sc);
  789. } else {
  790. sc = 0;
  791. }
  792. // Change ed from byte offset to codepoint index relative to sd
  793. MBCPtoWCCP(ped, pText+sd*ped->cbChar, ed-sd, &ed);
  794. if (fLeft && sc) // Going left
  795. sc--;
  796. if (fWordStop) {
  797. while (sc && !psa->pLogAttr[sc].fSoftBreak)
  798. sc--;
  799. }
  800. else {
  801. while (sc && !psa->pLogAttr[sc].fCharStop)
  802. sc--;
  803. }
  804. // Set ichMax to next stop
  805. ec = sc;
  806. if (ec < ed) {
  807. ec++;
  808. if (fWordStop) {
  809. while (ec < ed && !psa->pLogAttr[ec].fSoftBreak)
  810. ec++;
  811. }
  812. else {
  813. while (ec < ed && !psa->pLogAttr[ec].fCharStop)
  814. ec++;
  815. }
  816. }
  817. WCCPtoMBCP(ped, pText+sd*ped->cbChar, sc, &sc);
  818. WCCPtoMBCP(ped, pText+sd*ped->cbChar, ec, &ec);
  819. if (pichMin) *pichMin = sd + sc;
  820. if (pichMax) *pichMax = sd + ec;
  821. ScriptStringFree(&psa);
  822. } else {
  823. ASSERTHR(hr, ("EditGetNextBoundaries - LpkStringAnalyse"));
  824. // Analysis not possible - ignore content of complex scripts.
  825. if (pichMin) *pichMin = sd;
  826. if (pichMax) *pichMax = ed;
  827. }
  828. }
  829. //// EditNextWord - find adjacent word start and end points
  830. //
  831. // Duplicates the behaviour of US notepad.
  832. //
  833. // First stage identify word range using standard
  834. // blank/tab as delimiter.
  835. //
  836. // Second stage - analyse this run and use the logical
  837. // attributes to narrow down on words identified by
  838. // contextual processing in the complex script shaping
  839. // engines.
  840. //
  841. //
  842. // The following diagram describes the identification
  843. // of the initial character of the nearest word:
  844. //
  845. // GOING LEFT:
  846. //
  847. // Words WWWW WWWW WWWW
  848. // from any of xxxxxxxx
  849. // to x
  850. //
  851. // (Notice that the result is always to the left of the initial
  852. // position).
  853. //
  854. //
  855. // GOING RIGHT:
  856. //
  857. // Words WWWW WWWW WWWW
  858. // from any of xxxxxxxx
  859. // to x
  860. //
  861. //
  862. // Note that CRLF and CRCRLF are treated as words even if not
  863. // delimited by blanks.
  864. void EditNextWord(
  865. PED ped,
  866. HDC hdc,
  867. PSTR pText,
  868. ICH ichStart,
  869. BOOL fLeft,
  870. ICH *pichMin,
  871. ICH *pichMax)
  872. {
  873. EditGetNextBoundaries(ped, hdc, pText, ichStart, fLeft, pichMin, pichMax, TRUE);
  874. }
  875. //// IsVietnameseSequenceValid
  876. //
  877. // Borrow this code from richedit. The logic was provided by Chau Vu.
  878. //
  879. // April 26, 1999 [wchao]
  880. BOOL IsVietnameseSequenceValid (WCHAR ch1, WCHAR ch2)
  881. {
  882. #define IN_RANGE(n1, b, n2) ((unsigned)((b) - (n1)) <= (unsigned)((n2) - (n1)))
  883. int i;
  884. static const BYTE vowels[] = {0xF4, 0xEA, 0xE2, 'y', 'u', 'o', 'i', 'e', 'a'};
  885. if (!IN_RANGE(0x300, ch2, 0x323) || // Fast out
  886. !IN_RANGE(0x300, ch2, 0x301) && ch2 != 0x303 && ch2 != 0x309 && ch2 != 0x323)
  887. {
  888. return TRUE; // Not Vietnamese tone mark
  889. }
  890. for(i = sizeof(vowels) / sizeof(vowels[0]); i--;)
  891. if((ch1 | 0x20) == vowels[i]) // Vietnamese tone mark follows
  892. return TRUE; // vowel
  893. return IN_RANGE(0x102, ch1, 0x103) || // A-breve, a-breve
  894. IN_RANGE(0x1A0, ch1, 0x1A1) || // O-horn, o-horn
  895. IN_RANGE(0x1AF, ch1, 0x1B0); // U-horn, u-horn
  896. }
  897. //// EditStringValidate
  898. //
  899. // Validate the string sequence from the insertion point onward.
  900. // Return S_FALSE if any character beyond the insertion point produces fInvalid.
  901. //
  902. // April 5,1999 [wchao]
  903. HRESULT EditStringValidate (STRING_ANALYSIS* psa, int ichInsert)
  904. {
  905. BOOL fVietnameseCheck = PRIMARYLANGID(THREAD_HKL()) == LANG_VIETNAMESE;
  906. int iItem;
  907. int i;
  908. int l;
  909. if (!psa->pLogAttr)
  910. return E_INVALIDARG;
  911. for (iItem = 0; iItem < psa->cItems; iItem++)
  912. {
  913. if (g_ppScriptProperties[psa->pItems[iItem].a.eScript]->fRejectInvalid)
  914. {
  915. i = psa->pItems[iItem].iCharPos;
  916. l = psa->pItems[iItem + 1].iCharPos - i;
  917. while (l)
  918. {
  919. if (i >= ichInsert && psa->pLogAttr[i].fInvalid)
  920. return S_FALSE;
  921. i++;
  922. l--;
  923. }
  924. }
  925. else if (fVietnameseCheck && g_ppScriptProperties[psa->pItems[iItem].a.eScript]->fCDM)
  926. {
  927. // Vietnamese specific sequence check
  928. i = psa->pItems[iItem].iCharPos;
  929. l = psa->pItems[iItem + 1].iCharPos - i;
  930. while (l)
  931. {
  932. if (i > 0 && i >= ichInsert && !IsVietnameseSequenceValid(psa->pwInChars[i-1], psa->pwInChars[i]))
  933. return S_FALSE;
  934. i++;
  935. l--;
  936. }
  937. }
  938. }
  939. return S_OK;
  940. }
  941. //// EditVerifyText
  942. //
  943. // Verify the sequence of input text at the insertion point by calling
  944. // shaping engine that will return output flag pLogAttr->fInvalid in
  945. // the invalid position of text. Return 0 if the insert text has invalid
  946. // combination.
  947. //
  948. // Mar 31,1997 [wchao]
  949. INT EditVerifyText (PED ped, HDC hdc, PSTR pText, ICH ichInsert, PSTR pInsertText, ICH cchInsert) {
  950. ICH ichRunStart;
  951. ICH ichRunEnd;
  952. ICH ichLineStart;
  953. ICH ichLineEnd;
  954. ICH cchVerify;
  955. PSTR pVerify;
  956. INT iResult;
  957. UINT cbChar;
  958. BOOL fLocateChar;
  959. HRESULT hr;
  960. STRING_ANALYSIS *psa;
  961. ASSERTS(cchInsert > 0 && pInsertText != NULL && pText != NULL, ("Invalid parameters!"));
  962. if (cchInsert > 1)
  963. // What we concern here is about how should we handle a series of characters
  964. // forming invalid combination(s) that gets updated to the backing store as one
  965. // operation (e.g. pasting). We chose to not handle it and the logic is that to
  966. // -only- validate a given input char against the current state of the backing store.
  967. //
  968. // Notice that i use the value 1 so we're safe if inserted text is a DBCS character.
  969. //
  970. // [Dec 10, 98, wchao]
  971. return TRUE;
  972. if (ped->fSingle) {
  973. ichLineStart = 0;
  974. ichLineEnd = ped->cch;
  975. } else {
  976. ichLineStart = ped->chLines[ped->iCaretLine];
  977. ichLineEnd = ped->iCaretLine == ped->cLines-1 ? ped->cch : ped->chLines[ped->iCaretLine+1];
  978. }
  979. ichRunEnd = ichRunStart = ichInsert; // insertion point
  980. // Are we at the space?
  981. fLocateChar = EDWCH(ichInsert) == SP ? TRUE : FALSE;
  982. // Locate a valid char
  983. while ( ichRunStart > ichLineStart && fLocateChar && EDWCH(ichRunStart) == SP ) {
  984. ichRunStart--;
  985. }
  986. // Locate run starting point
  987. // Find space
  988. while (ichRunStart > ichLineStart && EDWCH(ichRunStart - 1) != SP) {
  989. ichRunStart--;
  990. }
  991. // Cover leading spaces
  992. while (ichRunStart > ichLineStart && EDWCH(ichRunStart - 1) == SP) {
  993. ichRunStart--;
  994. }
  995. // Locate a valid char
  996. while ( ichRunEnd < ichLineEnd && fLocateChar && EDWCH(ichRunEnd) == SP ) {
  997. ichRunEnd++;
  998. }
  999. // Locate run ending point
  1000. while (ichRunEnd < ichLineEnd && EDWCH(ichRunEnd) != SP) {
  1001. ichRunEnd++;
  1002. }
  1003. ASSERTS(ichRunStart <= ichRunEnd, "Invalid run length!");
  1004. // Merge insert text with insertion point run.
  1005. cchVerify = ichRunEnd - ichRunStart + cchInsert;
  1006. cbChar = ped->cbChar;
  1007. pVerify = (PSTR) GlobalAlloc(GMEM_FIXED, cchVerify * cbChar);
  1008. if (pVerify) {
  1009. PSTR pv;
  1010. UINT cbCopy;
  1011. pv = pVerify;
  1012. cbCopy = (ichInsert - ichRunStart) * cbChar;
  1013. memcpy (pv, pText + ichRunStart * cbChar, cbCopy);
  1014. pv += cbCopy;
  1015. cbCopy = cchInsert * cbChar;
  1016. memcpy (pv, pInsertText, cbCopy);
  1017. pv += cbCopy;
  1018. cbCopy = (ichRunEnd - ichInsert) * cbChar;
  1019. memcpy (pv, pText + ichInsert * cbChar, cbCopy);
  1020. } else {
  1021. ASSERTS(pVerify, "EditVerifyText: Assertion failure: Could not allocate merge text buffer");
  1022. return 1; // can do nothing now just simply accept it.
  1023. }
  1024. psa = NULL;
  1025. iResult = TRUE; // assume verification pass.
  1026. // Do the real work.
  1027. // This will call to shaping engine and proceed item by item.
  1028. //
  1029. hr = LpkStringAnalyse(
  1030. hdc, pVerify, cchVerify, 0,
  1031. GetEditAnsiConversionCharset(ped),
  1032. SSA_BREAK
  1033. | (ped->charPasswordChar ? SSA_PASSWORD : 0)
  1034. | (ped->fRtoLReading ? SSA_RTL : 0),
  1035. -1, 0,
  1036. NULL, NULL, NULL, NULL, NULL,
  1037. &psa);
  1038. if (SUCCEEDED(hr)) {
  1039. MBCPtoWCCP(ped, pVerify, ichInsert - ichRunStart, &ichInsert);
  1040. hr = EditStringValidate(psa, ichInsert);
  1041. if (hr == S_FALSE) {
  1042. MessageBeep((UINT)-1);
  1043. iResult = FALSE;
  1044. } else if (FAILED(hr)) {
  1045. ASSERTHR(hr, ("EditVerifyText - EditStringValidate"));
  1046. }
  1047. ScriptStringFree(&psa);
  1048. } else {
  1049. ASSERTHR(hr, ("EditVerifyText - LpkStringAnalyse"));
  1050. }
  1051. GlobalFree((HGLOBAL) pVerify);
  1052. return iResult;
  1053. }
  1054. //// EditProcessMenu
  1055. //
  1056. // Process LPK context menu commands.
  1057. //
  1058. // April 18, 1997 [wchao]
  1059. //
  1060. INT EditProcessMenu (PED ped, UINT idMenuItem)
  1061. {
  1062. HWND hwnd;
  1063. INT iResult;
  1064. iResult = TRUE;
  1065. switch (idMenuItem) {
  1066. case ID_CNTX_RTL:
  1067. hwnd = ped->hwnd;
  1068. SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~ES_FMTMASK);
  1069. if (!ped->fRtoLReading) {
  1070. SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE)
  1071. | (WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
  1072. }
  1073. else {
  1074. SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE)
  1075. & ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
  1076. }
  1077. break;
  1078. case ID_CNTX_DISPLAYCTRL:
  1079. hwnd = ped->hwnd;
  1080. ped->fDisplayCtrl = !ped->fDisplayCtrl;
  1081. if (ped->fFlatBorder) {
  1082. RECT rcT;
  1083. int cxBorder, cyBorder;
  1084. GetClientRect(hwnd, &rcT);
  1085. cxBorder = GetSystemMetrics (SM_CXBORDER);
  1086. cyBorder = GetSystemMetrics (SM_CYBORDER);
  1087. InflateRect(&rcT, -cxBorder, -cyBorder);
  1088. InvalidateRect(hwnd, &rcT, TRUE);
  1089. }
  1090. else {
  1091. InvalidateRect(hwnd, NULL, TRUE);
  1092. }
  1093. break;
  1094. case ID_CNTX_ZWNJ:
  1095. if (ped->fAnsi) {
  1096. SendMessageA(ped->hwnd, WM_CHAR, 0x9D, 0);
  1097. } else {
  1098. SendMessageW(ped->hwnd, WM_CHAR, U_ZWNJ, 0);
  1099. }
  1100. break;
  1101. case ID_CNTX_ZWJ:
  1102. if (ped->fAnsi) {
  1103. SendMessageA(ped->hwnd, WM_CHAR, 0x9E, 0);
  1104. } else {
  1105. SendMessageW(ped->hwnd, WM_CHAR, U_ZWJ, 0);
  1106. }
  1107. break;
  1108. case ID_CNTX_LRM:
  1109. if (ped->fAnsi) {
  1110. SendMessageA(ped->hwnd, WM_CHAR, 0xFD, 0);
  1111. } else {
  1112. SendMessageW(ped->hwnd, WM_CHAR, U_LRM, 0);
  1113. }
  1114. break;
  1115. case ID_CNTX_RLM:
  1116. if (ped->fAnsi) {
  1117. SendMessageA(ped->hwnd, WM_CHAR, 0xFE, 0);
  1118. } else {
  1119. SendMessageW(ped->hwnd, WM_CHAR, U_RLM, 0);
  1120. }
  1121. break;
  1122. case ID_CNTX_LRE: SendMessageW(ped->hwnd, WM_CHAR, U_LRE, 0); break;
  1123. case ID_CNTX_RLE: SendMessageW(ped->hwnd, WM_CHAR, U_RLE, 0); break;
  1124. case ID_CNTX_LRO: SendMessageW(ped->hwnd, WM_CHAR, U_LRO, 0); break;
  1125. case ID_CNTX_RLO: SendMessageW(ped->hwnd, WM_CHAR, U_RLO, 0); break;
  1126. case ID_CNTX_PDF: SendMessageW(ped->hwnd, WM_CHAR, U_PDF, 0); break;
  1127. case ID_CNTX_NADS: SendMessageW(ped->hwnd, WM_CHAR, U_NADS, 0); break;
  1128. case ID_CNTX_NODS: SendMessageW(ped->hwnd, WM_CHAR, U_NODS, 0); break;
  1129. case ID_CNTX_ASS: SendMessageW(ped->hwnd, WM_CHAR, U_ASS, 0); break;
  1130. case ID_CNTX_ISS: SendMessageW(ped->hwnd, WM_CHAR, U_ISS, 0); break;
  1131. case ID_CNTX_AAFS: SendMessageW(ped->hwnd, WM_CHAR, U_AAFS, 0); break;
  1132. case ID_CNTX_IAFS: SendMessageW(ped->hwnd, WM_CHAR, U_IAFS, 0); break;
  1133. case ID_CNTX_RS: SendMessageW(ped->hwnd, WM_CHAR, U_RS, 0); break;
  1134. case ID_CNTX_US: SendMessageW(ped->hwnd, WM_CHAR, U_US, 0); break;
  1135. }
  1136. return iResult;
  1137. }
  1138. //// EditSetMenu - Set menu state
  1139. //
  1140. //
  1141. void EditSetMenu(PED ped, HMENU hMenu) {
  1142. EnableMenuItem(hMenu, ID_CNTX_RTL, MF_BYCOMMAND | MF_ENABLED);
  1143. CheckMenuItem (hMenu, ID_CNTX_RTL, MF_BYCOMMAND | (ped->fRtoLReading ? MF_CHECKED : MF_UNCHECKED));
  1144. if (!ped->fAnsi || ped->charSet == ARABIC_CHARSET || ped->charSet == HEBREW_CHARSET) {
  1145. // It's unicode, Arabic or Hebrew - we can display and enter at least some control characters
  1146. EnableMenuItem(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | MF_ENABLED);
  1147. CheckMenuItem (hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | (ped->fDisplayCtrl ? MF_CHECKED : MF_UNCHECKED));
  1148. EnableMenuItem(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND | MF_ENABLED);
  1149. EnableMenuItem(hMenu, ID_CNTX_LRM, MF_BYCOMMAND | MF_ENABLED);
  1150. EnableMenuItem(hMenu, ID_CNTX_RLM, MF_BYCOMMAND | MF_ENABLED);
  1151. if (!ped->fAnsi || ped->charSet == ARABIC_CHARSET) {
  1152. // Controls characters in Unicode and ANSI Arabic only
  1153. EnableMenuItem(hMenu, ID_CNTX_ZWJ, MF_BYCOMMAND | MF_ENABLED);
  1154. EnableMenuItem(hMenu, ID_CNTX_ZWNJ, MF_BYCOMMAND | MF_ENABLED);
  1155. }
  1156. if (!ped->fAnsi) {
  1157. // These control characters are specific to the Unicode bidi algorithm
  1158. EnableMenuItem(hMenu, ID_CNTX_LRE, MF_BYCOMMAND | MF_ENABLED);
  1159. EnableMenuItem(hMenu, ID_CNTX_RLE, MF_BYCOMMAND | MF_ENABLED);
  1160. EnableMenuItem(hMenu, ID_CNTX_LRO, MF_BYCOMMAND | MF_ENABLED);
  1161. EnableMenuItem(hMenu, ID_CNTX_RLO, MF_BYCOMMAND | MF_ENABLED);
  1162. EnableMenuItem(hMenu, ID_CNTX_PDF, MF_BYCOMMAND | MF_ENABLED);
  1163. EnableMenuItem(hMenu, ID_CNTX_NADS, MF_BYCOMMAND | MF_ENABLED);
  1164. EnableMenuItem(hMenu, ID_CNTX_NODS, MF_BYCOMMAND | MF_ENABLED);
  1165. EnableMenuItem(hMenu, ID_CNTX_ASS, MF_BYCOMMAND | MF_ENABLED);
  1166. EnableMenuItem(hMenu, ID_CNTX_ISS, MF_BYCOMMAND | MF_ENABLED);
  1167. EnableMenuItem(hMenu, ID_CNTX_AAFS, MF_BYCOMMAND | MF_ENABLED);
  1168. EnableMenuItem(hMenu, ID_CNTX_IAFS, MF_BYCOMMAND | MF_ENABLED);
  1169. EnableMenuItem(hMenu, ID_CNTX_RS, MF_BYCOMMAND | MF_ENABLED);
  1170. EnableMenuItem(hMenu, ID_CNTX_US, MF_BYCOMMAND | MF_ENABLED);
  1171. }
  1172. } else {
  1173. // No opportunity to enter control characters
  1174. EnableMenuItem(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND | MF_GRAYED);
  1175. EnableMenuItem(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | MF_GRAYED);
  1176. }
  1177. }
  1178. //// EditCreateCaretFromFont
  1179. //
  1180. // Create one of the special caret shapes for complex script languages.
  1181. //
  1182. // returns FALSE if it couldn't create the caret for example in low
  1183. // memory situations.
  1184. #define CURSOR_USA 0xffff
  1185. #define CURSOR_LTR 0xf00c
  1186. #define CURSOR_RTL 0xf00d
  1187. #define CURSOR_THAI 0xf00e
  1188. BOOL EditCreateCaretFromFont(
  1189. PED ped,
  1190. HDC hdc,
  1191. INT nWidth,
  1192. INT nHeight,
  1193. WCHAR wcCursorCode
  1194. )
  1195. {
  1196. BOOL fResult = FALSE; // Assume the worst
  1197. HBITMAP hbmBits;
  1198. HDC hcdcBits;
  1199. HFONT hArrowFont;
  1200. ABC abcWidth;
  1201. COLORREF clrBk;
  1202. WORD gidArrow;
  1203. UINT uiWidthBits;
  1204. HBITMAP hOldBitmap;
  1205. // Create caret from the Arial font
  1206. hcdcBits = CreateCompatibleDC (hdc);
  1207. if (!hcdcBits)
  1208. {
  1209. return FALSE;
  1210. }
  1211. // create Arrow font then select into compatible DC
  1212. // Bitmap will be XORed with the background color before caret starts blinking.
  1213. // Therefore, we need to set our bitmap background to be opposite with the DC
  1214. // actual background in order to generate caret properly.
  1215. clrBk = GetBkColor(hdc);
  1216. SetBkColor (hcdcBits, ~clrBk);
  1217. // Creating the caret with white pattern to be consistant with User-edit field.
  1218. clrBk = RGB(255, 255 , 255); // White
  1219. SetTextColor (hcdcBits, clrBk);
  1220. hArrowFont = CreateFontW ( nHeight, 0, 0, 0, nWidth > 1 ? 700 : 400, 0L, 0L, 0L, 1L,
  1221. 0L, 0L, 0L, 0L, L"Microsoft Sans Serif" );
  1222. if (!hArrowFont)
  1223. {
  1224. goto error;
  1225. }
  1226. SelectObject (hcdcBits, hArrowFont);
  1227. // textout the Arrow char to get its bitmap
  1228. if (!GetCharABCWidthsW (hcdcBits, wcCursorCode, wcCursorCode, &abcWidth))
  1229. {
  1230. goto error;
  1231. }
  1232. if (!GetGlyphIndicesW (hcdcBits, &wcCursorCode, 1, &gidArrow, 0))
  1233. {
  1234. goto error;
  1235. }
  1236. uiWidthBits = (((abcWidth.abcB)+15)/16)*16; // bitmap width must be WORD-aligned
  1237. hbmBits = CreateCompatibleBitmap (hdc, uiWidthBits, nHeight);
  1238. if (!hbmBits)
  1239. {
  1240. goto error;
  1241. }
  1242. hOldBitmap = SelectObject(hcdcBits, hbmBits);
  1243. if (!ExtTextOutW (hcdcBits, -abcWidth.abcA, 0, ETO_OPAQUE | ETO_GLYPH_INDEX, NULL, &gidArrow, 1, NULL))
  1244. {
  1245. DeleteObject(SelectObject(hcdcBits, hOldBitmap));
  1246. goto error;
  1247. }
  1248. // free current caret bitmap handle if we have one
  1249. if (ped->hCaretBitmap) {
  1250. DeleteObject (ped->hCaretBitmap);
  1251. }
  1252. ped->hCaretBitmap = hbmBits;
  1253. if (wcCursorCode == CURSOR_RTL) {
  1254. // RTL cursor has vertical stroke on right hand side. Overlap LTR and RTL
  1255. // positions by one pixel so cursor doesn't go outside the edit control
  1256. // at small sizes.
  1257. ped->iCaretOffset = 1 - (int) abcWidth.abcB; // (Allow one pixel overlap between ltr & rtl)
  1258. } else {
  1259. ped->iCaretOffset = 0;
  1260. }
  1261. fResult = CreateCaret (ped->hwnd, hbmBits, 0, 0);
  1262. error:
  1263. // release allocated objects
  1264. if (hArrowFont)
  1265. {
  1266. DeleteObject(hArrowFont);
  1267. }
  1268. if (hcdcBits)
  1269. {
  1270. DeleteDC(hcdcBits);
  1271. }
  1272. return fResult;
  1273. }
  1274. //// EditCreateCaret
  1275. //
  1276. // Create locale specific caret shape
  1277. //
  1278. // April 25, 1997 [wchao]
  1279. // May 1st, 1997 [samera] Added traditional BiDi cursor under #ifdef
  1280. // Aug 15th, 2000 [dbrown] Fix prefix bug 43057 - no handling for low memory
  1281. //
  1282. // Note
  1283. // Complex script carets are mapped in the private area of Unicode in the font.
  1284. // LTR cursor 0xf00c
  1285. // RTL cursor 0xf00d
  1286. // Thai cursor 0xf00e
  1287. //
  1288. #define LANG_ID(x) ((DWORD)(DWORD_PTR)x & 0x000003ff);
  1289. INT EditCreateCaret(
  1290. PED ped,
  1291. HDC hdc,
  1292. INT nWidth,
  1293. INT nHeight,
  1294. UINT hklCurrent) {
  1295. UINT uikl;
  1296. ULONG ulCsrCacheCount;
  1297. WCHAR wcCursorCode;
  1298. ped->iCaretOffset = 0;
  1299. if (!hklCurrent) {
  1300. uikl = LANG_ID(GetKeyboardLayout(0L));
  1301. } else {
  1302. uikl = LANG_ID(hklCurrent);
  1303. }
  1304. // Choose caret shape - use either the standard US caret, or a
  1305. // special shape from the Arial font.
  1306. wcCursorCode = CURSOR_USA;
  1307. switch (uikl) {
  1308. case LANG_THAI: wcCursorCode = CURSOR_THAI; break;
  1309. //
  1310. // we may need to call GetLocaleInfo( FONT_SIGNATURE ...) to
  1311. // properly detect RTL languages.
  1312. //
  1313. case LANG_ARABIC:
  1314. case LANG_FARSI:
  1315. case LANG_URDU:
  1316. case LANG_HEBREW: wcCursorCode = CURSOR_RTL; break;
  1317. default:
  1318. // Make sure the NLS settings are cached before checking on g_UserBidiLocale. it happens!
  1319. if ( g_ulNlsUpdateCacheCount==-1
  1320. && (ulCsrCacheCount = NlsGetCacheUpdateCount()) != g_ulNlsUpdateCacheCount) {
  1321. TRACE(NLS, ("LPK : Updating NLS cache from EditCreateCaret, lpkNlsCacheCount=%ld, CsrssCacheCount=%ld",
  1322. g_ulNlsUpdateCacheCount ,ulCsrCacheCount));
  1323. g_ulNlsUpdateCacheCount = ulCsrCacheCount;
  1324. // Update the cache now
  1325. ReadNLSScriptSettings();
  1326. }
  1327. if (g_UserBidiLocale) {
  1328. // Other keyboards have a left-to-right pointing caret
  1329. // in Bidi locales.
  1330. wcCursorCode = CURSOR_LTR;
  1331. }
  1332. }
  1333. if (wcCursorCode != CURSOR_USA)
  1334. {
  1335. // Try to create a caret from the Arial font
  1336. if (!EditCreateCaretFromFont(ped, hdc, nWidth, nHeight, wcCursorCode))
  1337. {
  1338. // Caret from font failed - low memory perhaps
  1339. wcCursorCode = CURSOR_USA; // Fall back to USA cursor
  1340. }
  1341. }
  1342. if (wcCursorCode == CURSOR_USA) {
  1343. // Use Windows default caret
  1344. return CreateCaret (ped->hwnd, NULL, nWidth, nHeight);
  1345. } else {
  1346. return TRUE;
  1347. }
  1348. }
  1349. //// EditAdjustCaret
  1350. //
  1351. // Adjust caret after insertion/deletion to avoid the caret in between a cluster,
  1352. // very common in Indic e.g.
  1353. //
  1354. // 1. Deleting the space "X| Y" and it becomes "X|y".
  1355. // 2. Inserting 'X' at "|Y" and it becomes "X|y".
  1356. //
  1357. // May 3,1999 [wchao]
  1358. //
  1359. INT EditAdjustCaret (
  1360. PED ped,
  1361. HDC hdc,
  1362. PSTR pText,
  1363. ICH ich)
  1364. {
  1365. #if 0
  1366. //
  1367. // Indiannt unanimously request this feature to be removed for final product.
  1368. // (wchao - 7/12/99)
  1369. //
  1370. ICH ichMin;
  1371. ICH ichMax;
  1372. if (ich < ped->cch)
  1373. {
  1374. EditGetNextBoundaries(ped, hdc, pText, ich, FALSE, &ichMin, &ichMax, FALSE);
  1375. if (ich > ichMin)
  1376. ich = ichMax;
  1377. }
  1378. #endif
  1379. UNREFERENCED_PARAMETER(ped);
  1380. UNREFERENCED_PARAMETER(hdc);
  1381. UNREFERENCED_PARAMETER(pText);
  1382. return ich;
  1383. }
  1384. //// Edit callout
  1385. //
  1386. // The LPK edit support functions are acessed by the edit
  1387. // control code through a struct defined in user.h.
  1388. //
  1389. // Here we initialise that struct with the addresses
  1390. // of the callout functions.
  1391. LPKEDITCALLOUT LpkEditControl = {
  1392. EditCreate,
  1393. EditIchToXY,
  1394. EditMouseToIch,
  1395. EditCchInWidth,
  1396. EditGetLineWidth,
  1397. EditDrawText,
  1398. EditHScroll,
  1399. EditMoveSelection,
  1400. EditVerifyText,
  1401. EditNextWord,
  1402. EditSetMenu,
  1403. EditProcessMenu,
  1404. EditCreateCaret,
  1405. EditAdjustCaret
  1406. };