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.

4353 lines
138 KiB

  1. /***************************************************************************\
  2. * editml.c - Edit controls rewrite. Version II of edit controls.
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Multi-Line Support Routines
  7. *
  8. * Created: 24-Jul-88 davidds
  9. \***************************************************************************/
  10. #include "precomp.h"
  11. #pragma hdrstop
  12. /*
  13. * Number of lines to bump when reallocating index buffer
  14. */
  15. #define LINEBUMP 32
  16. /*
  17. * Code to catch bug #202678, please remove it get resolved.
  18. */
  19. #define CHECK_LINE_NUMBER(iLine, ped) \
  20. if (((int)iLine < 0) || ((ICH)iLine >= ped->cLines)) { \
  21. FRE_RIPMSG0(RIP_ERROR, "Line # is < 0 or >= ped->cLines, Please check this out!"); \
  22. }
  23. /*
  24. * Used for ML scroll updates
  25. */
  26. #define ML_REFRESH 0xffffffff
  27. __inline void MLSanityCheck(PED ped)
  28. {
  29. UNREFERENCED_PARAMETER(ped); // For free build
  30. UserAssert(ped->cch >= ped->chLines[ped->cLines - 1]);
  31. }
  32. /***************************************************************************\
  33. *
  34. * MLGetLineWidth()
  35. *
  36. * Returns the max width in a line. ECTabTheTextOut() ensures that max
  37. * width won't overflow.
  38. *
  39. \***************************************************************************/
  40. UINT MLGetLineWidth(HDC hdc, LPSTR lpstr, int nCnt, PED ped)
  41. {
  42. return(ECTabTheTextOut(hdc, 0, 0, 0, 0, lpstr, nCnt, 0, ped, 0, ECT_CALC, NULL));
  43. }
  44. /***************************************************************************\
  45. *
  46. * MLSize()
  47. *
  48. * Handles resizing of the edit control window and updating thereof.
  49. *
  50. * Sets the edit field's formatting area given the passed in "client area".
  51. * We fudge it if it doesn't seem reasonable.
  52. *
  53. \***************************************************************************/
  54. void MLSize(PED ped, BOOL fRedraw)
  55. {
  56. // Calculate the # of lines we can fit in our rectangle.
  57. ped->ichLinesOnScreen = (ped->rcFmt.bottom - ped->rcFmt.top) / ped->lineHeight;
  58. // Make the format rectangle height an integral number of lines
  59. ped->rcFmt.bottom = ped->rcFmt.top + ped->ichLinesOnScreen * ped->lineHeight;
  60. // Rebuild the line array
  61. if (ped->fWrap) {
  62. MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  63. MLUpdateiCaretLine(ped);
  64. } else {
  65. MLScroll(ped, TRUE, ML_REFRESH, 0, fRedraw);
  66. MLScroll(ped, FALSE, ML_REFRESH, 0, fRedraw);
  67. }
  68. }
  69. /***************************************************************************\
  70. * MLCalcXOffset AorW
  71. *
  72. * Calculates the horizontal offset (indent) required for centered
  73. * and right justified lines.
  74. *
  75. * History:
  76. *
  77. * Not used if language pack loaded.
  78. \***************************************************************************/
  79. int MLCalcXOffset(
  80. PED ped,
  81. HDC hdc,
  82. int lineNumber)
  83. {
  84. PSTR pText;
  85. ICH lineLength;
  86. ICH lineWidth;
  87. if (ped->format == ES_LEFT)
  88. return (0);
  89. lineLength = MLLine(ped, lineNumber);
  90. if (lineLength) {
  91. pText = ECLock(ped) + ped->chLines[lineNumber] * ped->cbChar;
  92. hdc = ECGetEditDC(ped, TRUE);
  93. lineWidth = MLGetLineWidth(hdc, pText, lineLength, ped);
  94. ECReleaseEditDC(ped, hdc, TRUE);
  95. ECUnlock(ped);
  96. } else {
  97. lineWidth = 0;
  98. }
  99. /*
  100. * If a SPACE or a TAB was eaten at the end of a line by MLBuildchLines
  101. * to prevent a delimiter appearing at the begining of a line, the
  102. * the following calculation will become negative causing this bug.
  103. * So, now, we take zero in such cases.
  104. * Fix for Bug #3566 --01/31/91-- SANKAR --
  105. */
  106. lineWidth = max(0, (int)(ped->rcFmt.right-ped->rcFmt.left-lineWidth));
  107. if (ped->format == ES_CENTER)
  108. return (lineWidth / 2);
  109. if (ped->format == ES_RIGHT) {
  110. /*
  111. * Subtract 1 so that the 1 pixel wide cursor will be in the visible
  112. * region on the very right side of the screen.
  113. */
  114. return max(0, (int)(lineWidth-1));
  115. }
  116. return 0;
  117. }
  118. /***************************************************************************\
  119. * MLMoveSelection AorW
  120. *
  121. * Moves the selection character in the direction indicated. Assumes
  122. * you are starting at a legal point, we decrement/increment the ich. Then,
  123. * This decrements/increments it some more to get past CRLFs...
  124. *
  125. * History:
  126. \***************************************************************************/
  127. ICH MLMoveSelection(
  128. PED ped,
  129. ICH ich,
  130. BOOL fLeft)
  131. {
  132. if (fLeft && ich > 0) {
  133. /*
  134. * Move left
  135. */
  136. ich = ECPrevIch( ped, NULL, ich );
  137. if (ich) {
  138. if (ped->fAnsi) {
  139. LPSTR pText;
  140. /*
  141. * Check for CRLF or CRCRLF
  142. */
  143. pText = ECLock(ped) + ich;
  144. /*
  145. * Move before CRLF or CRCRLF
  146. */
  147. if (*(WORD UNALIGNED *)(pText - 1) == 0x0A0D) {
  148. ich--;
  149. if (ich && *(pText - 2) == 0x0D)
  150. ich--;
  151. }
  152. ECUnlock(ped);
  153. } else { // !fAnsi
  154. LPWSTR pwText;
  155. /*
  156. * Check for CRLF or CRCRLF
  157. */
  158. pwText = (LPWSTR)ECLock(ped) + ich;
  159. /*
  160. * Move before CRLF or CRCRLF
  161. */
  162. if (*(pwText - 1) == 0x0D && *pwText == 0x0A) {
  163. ich--;
  164. if (ich && *(pwText - 2) == 0x0D)
  165. ich--;
  166. }
  167. ECUnlock(ped);
  168. }
  169. }
  170. } else if (!fLeft && ich < ped->cch) {
  171. /*
  172. * Move right.
  173. */
  174. ich = ECNextIch( ped, NULL, ich );
  175. if (ich < ped->cch) {
  176. if (ped->fAnsi) {
  177. LPSTR pText;
  178. pText = ECLock(ped) + ich;
  179. /*
  180. * Move after CRLF
  181. */
  182. if (*(WORD UNALIGNED *)(pText - 1) == 0x0A0D)
  183. ich++;
  184. else {
  185. /*
  186. * Check for CRCRLF
  187. */
  188. if (ich && *(WORD UNALIGNED *)pText == 0x0A0D && *(pText - 1) == 0x0D)
  189. ich += 2;
  190. }
  191. ECUnlock(ped);
  192. } else { // !fAnsi
  193. LPWSTR pwText;
  194. pwText = (LPWSTR)ECLock(ped) + ich;
  195. /*
  196. * Move after CRLF
  197. */
  198. if (*(pwText - 1) == 0x0D && *pwText == 0x0A)
  199. ich++;
  200. else {
  201. /*
  202. * Check for CRCRLF
  203. */
  204. if (ich && *(pwText - 1) == 0x0D && *pwText == 0x0D &&
  205. *(pwText + 1) == 0x0A)
  206. ich += 2;
  207. }
  208. ECUnlock(ped);
  209. }
  210. }
  211. }
  212. return (ich);
  213. }
  214. /***************************************************************************\
  215. * MLMoveSelectionRestricted AorW
  216. *
  217. * Moves the selection like MLMoveSelection, but also obeys limitations
  218. * imposed by some languages such as Thai, where the cursor cannot stop
  219. * between a character and it's attached vowel or tone marks.
  220. *
  221. * Only called if the language pack is loaded.
  222. *
  223. \***************************************************************************/
  224. /***************************************************************************\
  225. * MLMoveSelectionRestricted AorW
  226. *
  227. * Moves the selection like MLMoveSelection, but also obeys limitations
  228. * imposed by some languages such as Thai, where the cursor cannot stop
  229. * between a character and it's attached vowel or tone marks.
  230. *
  231. * Only called if the language pack is loaded.
  232. *
  233. \***************************************************************************/
  234. ICH MLMoveSelectionRestricted(
  235. PED ped,
  236. ICH ich,
  237. BOOL fLeft)
  238. {
  239. PSTR pText;
  240. HDC hdc;
  241. ICH ichResult;
  242. pText = ECLock(ped);
  243. hdc = ECGetEditDC(ped, TRUE);
  244. ichResult = ped->pLpkEditCallout->EditMoveSelection(ped, hdc, pText, ich, fLeft);
  245. ECReleaseEditDC(ped, hdc, TRUE);
  246. ECUnlock(ped);
  247. return ichResult;
  248. }
  249. /***************************************************************************\
  250. * MLSetCaretPosition AorW
  251. *
  252. * If the window has the focus, find where the caret belongs and move
  253. * it there.
  254. *
  255. * History:
  256. \***************************************************************************/
  257. void MLSetCaretPosition(
  258. PED ped,
  259. HDC hdc)
  260. {
  261. POINT position;
  262. BOOL prevLine;
  263. int x = -20000;
  264. int y = -20000;
  265. /*
  266. * We will only position the caret if we have the focus since we don't want
  267. * to move the caret while another window could own it.
  268. */
  269. if (!ped->fFocus || !_IsWindowVisible(ped->pwnd))
  270. return;
  271. /*
  272. * Find the position of the caret
  273. */
  274. if (!ped->fCaretHidden &&
  275. ((ICH) ped->iCaretLine >= ped->ichScreenStart) &&
  276. ((ICH) ped->iCaretLine < (ped->ichScreenStart + ped->ichLinesOnScreen))) {
  277. RECT rcRealFmt;
  278. if (ped->f40Compat)
  279. {
  280. GetClientRect(ped->hwnd, &rcRealFmt);
  281. IntersectRect(&rcRealFmt, &rcRealFmt, &ped->rcFmt);
  282. } else {
  283. CopyRect(&rcRealFmt, &ped->rcFmt);
  284. }
  285. if (ped->cLines - 1 != ped->iCaretLine && ped->ichCaret == ped->chLines[ped->iCaretLine + 1]) {
  286. prevLine = TRUE;
  287. } else {
  288. prevLine = FALSE;
  289. }
  290. MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine, &position);
  291. if ( (position.y >= rcRealFmt.top) &&
  292. (position.y <= rcRealFmt.bottom - ped->lineHeight)) {
  293. int xPos = position.x;
  294. int cxCaret = ECGetCaretWidth();
  295. if (ped->fWrap ||
  296. ((xPos > (rcRealFmt.left - cxCaret)) &&
  297. (xPos <= rcRealFmt.right))) {
  298. // Make sure the caret is in the visible region if word
  299. // wrapping. This is so that the caret will be visible if the
  300. // line ends with a space.
  301. x = max(xPos, rcRealFmt.left);
  302. x = min(x, rcRealFmt.right - cxCaret);
  303. y = position.y;
  304. }
  305. }
  306. }
  307. if (ped->pLpkEditCallout) {
  308. NtUserSetCaretPos(x + ped->iCaretOffset, y);
  309. } else {
  310. NtUserSetCaretPos(x, y);
  311. }
  312. // FE_IME : MLSetCaretPosition -- ImmSetCompositionWindow(CFS_RECT)
  313. if (fpImmIsIME(THREAD_HKL())) {
  314. if (x != -20000 && y != -20000) {
  315. ECImmSetCompositionWindow(ped, x, y);
  316. }
  317. }
  318. }
  319. /***************************************************************************\
  320. * MLLine
  321. *
  322. * Returns the length of the line (cch) given by lineNumber ignoring any
  323. * CRLFs in the line.
  324. *
  325. * History:
  326. \***************************************************************************/
  327. ICH MLLine(
  328. PED ped,
  329. ICH lineNumber)
  330. {
  331. ICH result;
  332. UserAssert(lineNumber < ped->cLines);
  333. if (lineNumber >= ped->cLines)
  334. return (0);
  335. if (lineNumber == ped->cLines - 1) {
  336. /*
  337. * Since we can't have a CRLF on the last line
  338. */
  339. return (ped->cch - ped->chLines[ped->cLines - 1]);
  340. } else {
  341. result = ped->chLines[lineNumber + 1] - ped->chLines[lineNumber];
  342. RIPMSG1(RIP_VERBOSE, "MLLine result=%d\n", result);
  343. /*
  344. * Now check for CRLF or CRCRLF at end of line
  345. */
  346. if (result > 1) {
  347. if (ped->fAnsi) {
  348. LPSTR pText;
  349. pText = ECLock(ped) + ped->chLines[lineNumber + 1] - 2;
  350. if (*(WORD UNALIGNED *)pText == 0x0A0D) {
  351. result -= 2;
  352. if (result && *(--pText) == 0x0D)
  353. /*
  354. * In case there was a CRCRLF
  355. */
  356. result--;
  357. }
  358. } else { // !fAnsi
  359. LPWSTR pwText;
  360. pwText = (LPWSTR)ECLock(ped) +
  361. (ped->chLines[lineNumber + 1] - 2);
  362. if (*(DWORD UNALIGNED *)pwText == 0x000A000D) {
  363. result = result - 2;
  364. if (result && *(--pwText) == 0x0D)
  365. /*
  366. * In case there was a CRCRLF
  367. */
  368. result--;
  369. }
  370. }
  371. ECUnlock(ped);
  372. }
  373. }
  374. return (result);
  375. }
  376. /***************************************************************************\
  377. * MLIchToLine AorW
  378. *
  379. * Returns the line number (starting from 0) which contains the given
  380. * character index. If ich is -1, return the line the first char in the
  381. * selection is on (the caret if no selection)
  382. *
  383. * History:
  384. \***************************************************************************/
  385. int MLIchToLine(
  386. PED ped,
  387. ICH ich)
  388. {
  389. int iLo, iHi, iLine;
  390. iLo = 0;
  391. iHi = ped->cLines;
  392. if (ich == (ICH)-1)
  393. ich = ped->ichMinSel;
  394. while (iLo < iHi - 1) {
  395. iLine = max((iHi - iLo)/2, 1) + iLo;
  396. if (ped->chLines[iLine] > ich) {
  397. iHi = iLine;
  398. } else {
  399. iLo = iLine;
  400. }
  401. }
  402. CHECK_LINE_NUMBER(iLo, ped);
  403. return iLo;
  404. }
  405. /***************************************************************************\
  406. * MLIchToYPos
  407. *
  408. * Given an ich, return its y coordinate with respect to the top line
  409. * displayed in the window. If prevLine is TRUE and if the ich is at the
  410. * beginning of the line, return the y coordinate of the
  411. * previous line (if it is not a CRLF).
  412. *
  413. * Added for the LPK (3Dec96) - with an LPK installed, calculating X position is
  414. * a far more processor intensive job. Where only the Y position is required
  415. * this routine should be called instead of MLIchToXYPos.
  416. *
  417. * Called only when LPK installed.
  418. *
  419. \***************************************************************************/
  420. /***************************************************************************\
  421. * MLIchToYPos
  422. *
  423. * Given an ich, return its y coordinate with respect to the top line
  424. * displayed in the window. If prevLine is TRUE and if the ich is at the
  425. * beginning of the line, return the y coordinate of the
  426. * previous line (if it is not a CRLF).
  427. *
  428. * Added for the LPK (3Dec96) - with an LPK installed, calculating X position is
  429. * a far more processor intensive job. Where only the Y position is required
  430. * this routine should be called instead of MLIchToXYPos.
  431. *
  432. * Called only when LPK installed.
  433. *
  434. \***************************************************************************/
  435. INT MLIchToYPos(
  436. PED ped,
  437. ICH ich,
  438. BOOL prevLine)
  439. {
  440. int iline;
  441. int yPosition;
  442. PSTR pText;
  443. /*
  444. * Determine what line the character is on
  445. */
  446. iline = MLIchToLine(ped, ich);
  447. /*
  448. * Calc. the yPosition now. Note that this may change by the height of one
  449. * char if the prevLine flag is set and the ICH is at the beginning of a
  450. * line.
  451. */
  452. yPosition = (iline - ped->ichScreenStart) * ped->lineHeight + ped->rcFmt.top;
  453. pText = ECLock(ped);
  454. if (prevLine && iline && (ich == ped->chLines[iline]) &&
  455. (!AWCOMPARECHAR(ped, pText + (ich - 2) * ped->cbChar, 0x0D) ||
  456. !AWCOMPARECHAR(ped, pText + (ich - 1) * ped->cbChar, 0x0A))) {
  457. /*
  458. * First char in the line. We want Y position of the previous
  459. * line if we aren't at the 0th line.
  460. */
  461. iline--;
  462. yPosition = yPosition - ped->lineHeight;
  463. }
  464. ECUnlock(ped);
  465. return yPosition;
  466. }
  467. /***************************************************************************\
  468. * MLIchToXYPos
  469. *
  470. * Given an ich, return its x,y coordinates with respect to the top
  471. * left character displayed in the window. Returns the coordinates of the top
  472. * left position of the char. If prevLine is TRUE then if the ich is at the
  473. * beginning of the line, we will return the coordinates to the right of the
  474. * last char on the previous line (if it is not a CRLF).
  475. *
  476. * History:
  477. \***************************************************************************/
  478. void MLIchToXYPos(
  479. PED ped,
  480. HDC hdc,
  481. ICH ich,
  482. BOOL prevLine,
  483. LPPOINT ppt)
  484. {
  485. int iline;
  486. ICH cch;
  487. int xPosition, yPosition;
  488. int xOffset;
  489. /*
  490. * For horizontal scroll displacement on left justified text and
  491. * for indent on centered or right justified text
  492. */
  493. PSTR pText, pTextStart, pLineStart;
  494. /*
  495. * Determine what line the character is on
  496. */
  497. iline = MLIchToLine(ped, ich);
  498. /*
  499. * Calc. the yPosition now. Note that this may change by the height of one
  500. * char if the prevLine flag is set and the ICH is at the beginning of a
  501. * line.
  502. */
  503. yPosition = (iline - ped->ichScreenStart) * ped->lineHeight + ped->rcFmt.top;
  504. /*
  505. * Now determine the xPosition of the character
  506. */
  507. pTextStart = ECLock(ped);
  508. if (prevLine && iline && (ich == ped->chLines[iline]) &&
  509. (!AWCOMPARECHAR(ped, pTextStart + (ich - 2) * ped->cbChar, 0x0D) ||
  510. !AWCOMPARECHAR(ped, pTextStart + (ich - 1) * ped->cbChar, 0x0A))) {
  511. /*
  512. * First char in the line. We want text extent upto end of the previous
  513. * line if we aren't at the 0th line.
  514. */
  515. iline--;
  516. yPosition = yPosition - ped->lineHeight;
  517. pLineStart = pTextStart + ped->chLines[iline] * ped->cbChar;
  518. /*
  519. * Note that we are taking the position in front of any CRLFs in the
  520. * text.
  521. */
  522. cch = MLLine(ped, iline);
  523. } else {
  524. pLineStart = pTextStart + ped->chLines[iline] * ped->cbChar;
  525. pText = pTextStart + ich * ped->cbChar;
  526. /*
  527. * Strip off CRLF or CRCRLF. Note that we may be pointing to a CR but in
  528. * which case we just want to strip off a single CR or 2 CRs.
  529. */
  530. /*
  531. * We want pText to point to the first CR at the end of the line if
  532. * there is one. Thus, we will get an xPosition to the right of the last
  533. * visible char on the line otherwise we will be to the left of
  534. * character ich.
  535. */
  536. /*
  537. * Check if we at the end of text
  538. */
  539. if (ich < ped->cch) {
  540. if (ped->fAnsi) {
  541. if (ich && *(WORD UNALIGNED *)(pText - 1) == 0x0A0D) {
  542. pText--;
  543. if (ich > 2 && *(pText - 1) == 0x0D)
  544. pText--;
  545. }
  546. } else {
  547. LPWSTR pwText = (LPWSTR)pText;
  548. if (ich && *(DWORD UNALIGNED *)(pwText - 1) == 0x000A000D) {
  549. pwText--;
  550. if (ich > 2 && *(pwText - 1) == 0x0D)
  551. pwText--;
  552. }
  553. pText = (LPSTR)pwText;
  554. }
  555. }
  556. if (pText < pLineStart)
  557. pText = pLineStart;
  558. cch = (ICH)(pText - pLineStart)/ped->cbChar;
  559. }
  560. /*
  561. * Find out how many pixels we indent the line for funny formats
  562. */
  563. if (ped->pLpkEditCallout) {
  564. /*
  565. * Must find position at start of character offset cch from start of line.
  566. * This depends on the layout and the reading order
  567. */
  568. xPosition = ped->pLpkEditCallout->EditIchToXY(
  569. ped, hdc, pLineStart, MLLine(ped, iline), cch);
  570. } else {
  571. if (ped->format != ES_LEFT) {
  572. xOffset = MLCalcXOffset(ped, hdc, iline);
  573. } else {
  574. xOffset = -(int)ped->xOffset;
  575. }
  576. xPosition = ped->rcFmt.left + xOffset +
  577. MLGetLineWidth(hdc, pLineStart, cch, ped);
  578. }
  579. ECUnlock(ped);
  580. ppt->x = xPosition;
  581. ppt->y = yPosition;
  582. return ;
  583. }
  584. /***************************************************************************\
  585. * MLMouseToIch AorW
  586. *
  587. * Returns the closest cch to where the mouse point is. Also optionally
  588. * returns lineindex in pline (So that we can tell if we are at the beginning
  589. * of the line or end of the previous line.)
  590. *
  591. * History:
  592. \***************************************************************************/
  593. ICH MLMouseToIch(
  594. PED ped,
  595. HDC hdc,
  596. LPPOINT mousePt,
  597. LPICH pline)
  598. {
  599. int xOffset;
  600. LPSTR pLineStart;
  601. int height = mousePt->y;
  602. int line; //WASINT
  603. int width = mousePt->x;
  604. ICH cch;
  605. ICH cLineLength;
  606. ICH cLineLengthNew;
  607. ICH cLineLengthHigh;
  608. ICH cLineLengthLow;
  609. ICH cLineLengthTemp;
  610. int textWidth;
  611. int iCurWidth;
  612. int lastHighWidth, lastLowWidth;
  613. /*
  614. * First determine which line the mouse is pointing to.
  615. */
  616. line = ped->ichScreenStart;
  617. if (height <= ped->rcFmt.top) {
  618. /*
  619. * Either return 0 (the very first line, or one line before the top line
  620. * on the screen. Note that these are signed mins and maxes since we
  621. * don't expect (or allow) more than 32K lines.
  622. */
  623. line = max(0, line-1);
  624. } else if (height >= ped->rcFmt.bottom) {
  625. /*
  626. * Are we below the last line displayed
  627. */
  628. line = min(line+(int)ped->ichLinesOnScreen, (int)(ped->cLines-1));
  629. } else {
  630. /*
  631. * We are somewhere on a line visible on screen
  632. */
  633. line = min(line + (int)((height - ped->rcFmt.top) / ped->lineHeight),
  634. (int)(ped->cLines - 1));
  635. }
  636. /*
  637. * Now determine what horizontal character the mouse is pointing to.
  638. */
  639. pLineStart = ECLock(ped) + ped->chLines[line] * ped->cbChar;
  640. cLineLength = MLLine(ped, line); /* Length is sans CRLF or CRCRLF */
  641. RIPMSG3(RIP_VERBOSE, "MLLine(ped=%x, line=%d) returned %d\n", ped, line, cLineLength);
  642. UserAssert((int)cLineLength >= 0);
  643. /*
  644. * If the language pack is loaded, visual and logical character order
  645. * may differ.
  646. */
  647. if (ped->pLpkEditCallout) {
  648. /*
  649. * Use the language pack to find the character nearest the cursor.
  650. */
  651. cch = ped->chLines[line] + ped->pLpkEditCallout->EditMouseToIch
  652. (ped, hdc, pLineStart, cLineLength, width);
  653. } else {
  654. /*
  655. * xOffset will be a negative value for center and right justified lines.
  656. * ie. We will just displace the lines left by the amount of indent for
  657. * right and center justification. Note that ped->xOffset will be 0 for
  658. * these lines since we don't support horizontal scrolling with them.
  659. */
  660. if (ped->format != ES_LEFT) {
  661. xOffset = MLCalcXOffset(ped, hdc, line);
  662. } else {
  663. /*
  664. * So that we handle a horizontally scrolled window for left justified
  665. * text.
  666. */
  667. xOffset = 0;
  668. }
  669. width = width - xOffset;
  670. /*
  671. * The code below is tricky... I depend on the fact that ped->xOffset is 0
  672. * for right and center justified lines
  673. */
  674. /*
  675. * Now find out how many chars fit in the given width
  676. */
  677. if (width >= ped->rcFmt.right) {
  678. /*
  679. * Return 1+last char in line or one plus the last char visible
  680. */
  681. cch = ECCchInWidth(ped, hdc, pLineStart, cLineLength,
  682. ped->rcFmt.right - ped->rcFmt.left + ped->xOffset, TRUE);
  683. //
  684. // Consider DBCS in case of width >= ped->rcFmt.right
  685. //
  686. // Since ECCchInWidth and MLLineLength takes care of DBCS, we only need to
  687. // worry about if the last character is a double byte character or not.
  688. //
  689. // cch = ped->chLines[line] + min( ECNextIch(ped, pLineStart, cch), cLineLength);
  690. //
  691. // we need to adjust the position. LiZ -- 5/5/93
  692. if (ped->fAnsi && ped->fDBCS) {
  693. ICH cch2 = min(cch+1,cLineLength);
  694. if (ECAdjustIch(ped, pLineStart, cch2) != cch2) {
  695. /* Displayed character on the right edge is DBCS */
  696. cch = min(cch+2,cLineLength);
  697. } else {
  698. cch = cch2;
  699. }
  700. cch += ped->chLines[line];
  701. } else {
  702. cch = ped->chLines[line] + min(cch + 1, cLineLength);
  703. }
  704. } else if (width <= ped->rcFmt.left + ped->aveCharWidth / 2) {
  705. /*
  706. * Return first char in line or one minus first char visible. Note that
  707. * ped->xOffset is 0 for right and centered text so we will just return
  708. * the first char in the string for them. (Allow a avecharwidth/2
  709. * positioning border so that the user can be a little off...
  710. */
  711. cch = ECCchInWidth(ped, hdc, pLineStart, cLineLength,
  712. ped->xOffset, TRUE);
  713. if (cch)
  714. cch--;
  715. cch = ECAdjustIch( ped, pLineStart, cch );
  716. cch += ped->chLines[line];
  717. } else {
  718. if (cLineLength == 0) {
  719. cch = ped->chLines[line];
  720. goto edUnlock;
  721. }
  722. iCurWidth = width + ped->xOffset - ped->rcFmt.left;
  723. /*
  724. * If the user clicked past the end of the text, return the last character
  725. */
  726. lastHighWidth = MLGetLineWidth(hdc, pLineStart, cLineLength, ped);
  727. if (lastHighWidth <= iCurWidth) {
  728. cLineLengthNew = cLineLength;
  729. goto edAdjust;
  730. }
  731. /*
  732. * Now the mouse is somewhere on the visible portion of the text
  733. * remember cch contains the length of the line.
  734. */
  735. cLineLengthLow = 0;
  736. cLineLengthHigh = cLineLength + 1;
  737. lastLowWidth = 0;
  738. while (cLineLengthLow < cLineLengthHigh - 1) {
  739. cLineLengthNew = (cLineLengthHigh + cLineLengthLow) / 2;
  740. if (ped->fAnsi && ped->fDBCS) {
  741. /*
  742. * MLGetLineWidth returns meaningless value for truncated DBCS.
  743. */
  744. cLineLengthTemp = ECAdjustIch(ped, pLineStart, cLineLengthNew);
  745. textWidth = MLGetLineWidth(hdc, pLineStart, cLineLengthTemp, ped);
  746. } else {
  747. textWidth = MLGetLineWidth(hdc, pLineStart, cLineLengthNew, ped);
  748. }
  749. if (textWidth > iCurWidth) {
  750. cLineLengthHigh = cLineLengthNew;
  751. lastHighWidth = textWidth;
  752. } else {
  753. cLineLengthLow = cLineLengthNew;
  754. lastLowWidth = textWidth;
  755. }
  756. }
  757. /*
  758. * When the while ends, you can't know the exact desired position.
  759. * Try to see if the mouse pointer was on the farest half
  760. * of the char we got and if so, adjust cch.
  761. */
  762. if (cLineLengthLow == cLineLengthNew) {
  763. /*
  764. * Need to compare with lastHighWidth
  765. */
  766. if ((lastHighWidth - iCurWidth) < (iCurWidth - textWidth)) {
  767. cLineLengthNew++;
  768. }
  769. } else {
  770. /*
  771. * Need to compare with lastLowHigh
  772. */
  773. if ((iCurWidth - lastLowWidth) < (textWidth - iCurWidth)) {
  774. cLineLengthNew--;
  775. }
  776. }
  777. edAdjust:
  778. cLineLength = ECAdjustIch( ped, pLineStart, cLineLengthNew );
  779. cch = ped->chLines[line] + cLineLength;
  780. }
  781. }
  782. edUnlock:
  783. ECUnlock(ped);
  784. if (pline) {
  785. CHECK_LINE_NUMBER(line, ped);
  786. *pline = line;
  787. }
  788. return cch;
  789. }
  790. /***************************************************************************\
  791. * MLChangeSelection AorW
  792. *
  793. * Changes the current selection to have the specified starting and
  794. * ending values. Properly highlights the new selection and unhighlights
  795. * anything deselected. If NewMinSel and NewMaxSel are out of order, we swap
  796. * them. Doesn't update the caret position.
  797. *
  798. * History:
  799. \***************************************************************************/
  800. void MLChangeSelection(
  801. PED ped,
  802. HDC hdc,
  803. ICH ichNewMinSel,
  804. ICH ichNewMaxSel)
  805. {
  806. ICH temp;
  807. ICH ichOldMinSel, ichOldMaxSel;
  808. if (ichNewMinSel > ichNewMaxSel) {
  809. temp = ichNewMinSel;
  810. ichNewMinSel = ichNewMaxSel;
  811. ichNewMaxSel = temp;
  812. }
  813. ichNewMinSel = min(ichNewMinSel, ped->cch);
  814. ichNewMaxSel = min(ichNewMaxSel, ped->cch);
  815. /*
  816. * Save the current selection
  817. */
  818. ichOldMinSel = ped->ichMinSel;
  819. ichOldMaxSel = ped->ichMaxSel;
  820. /*
  821. * Set new selection
  822. */
  823. ped->ichMinSel = ichNewMinSel;
  824. ped->ichMaxSel = ichNewMaxSel;
  825. /*
  826. * This finds the XOR of the old and new selection regions and redraws it.
  827. * There is nothing to repaint if we aren't visible or our selection
  828. * is hidden.
  829. */
  830. if (_IsWindowVisible(ped->pwnd) && (ped->fFocus || ped->fNoHideSel)) {
  831. BLOCK Blk[2];
  832. int i;
  833. if (ped->fFocus) {
  834. NtUserHideCaret(ped->hwnd);
  835. }
  836. Blk[0].StPos = ichOldMinSel;
  837. Blk[0].EndPos = ichOldMaxSel;
  838. Blk[1].StPos = ped->ichMinSel;
  839. Blk[1].EndPos = ped->ichMaxSel;
  840. if (ECCalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel, (LPBLOCK)&Blk[0], (LPBLOCK)&Blk[1])) {
  841. /*
  842. * Paint both Blk[0] and Blk[1], if they exist
  843. */
  844. for (i = 0; i < 2; i++) {
  845. if (Blk[i].StPos != 0xFFFFFFFF)
  846. MLDrawText(ped, hdc, Blk[i].StPos, Blk[i].EndPos, TRUE);
  847. }
  848. }
  849. /*
  850. * Update caret.
  851. */
  852. MLSetCaretPosition(ped, hdc);
  853. if (ped->fFocus) {
  854. NtUserShowCaret(ped->hwnd);
  855. }
  856. }
  857. }
  858. /**************************************************************************\
  859. * MLUpdateiCaretLine AorW
  860. *
  861. * This updates the ped->iCaretLine field from the ped->ichCaret;
  862. * Also, when the caret gets to the beginning of next line, pop it up to
  863. * the end of current line when inserting text;
  864. *
  865. * History
  866. * 4-18-91 Mikehar 31Merge
  867. \**************************************************************************/
  868. void MLUpdateiCaretLine(PED ped)
  869. {
  870. PSTR pText;
  871. ped->iCaretLine = MLIchToLine(ped, ped->ichCaret);
  872. /*
  873. * If caret gets to beginning of next line, pop it up to end of current line
  874. * when inserting text.
  875. */
  876. pText = ECLock(ped) +
  877. (ped->ichCaret - 1) * ped->cbChar;
  878. if (ped->iCaretLine && ped->chLines[ped->iCaretLine] == ped->ichCaret &&
  879. (!AWCOMPARECHAR(ped, pText - ped->cbChar, 0x0D) ||
  880. !AWCOMPARECHAR(ped, pText, 0x0A)))
  881. ped->iCaretLine--;
  882. ECUnlock(ped);
  883. }
  884. /***************************************************************************\
  885. * MLInsertText AorW
  886. *
  887. * Adds up to cchInsert characters from lpText to the ped starting at
  888. * ichCaret. If the ped only allows a maximum number of characters, then we
  889. * will only add that many characters to the ped. The number of characters
  890. * actually added is return ed (could be 0). If we can't allocate the required
  891. * space, we notify the parent with EN_ERRSPACE and no characters are added.
  892. * We will rebuild the lines array as needed. fUserTyping is true if the
  893. * input was the result of the user typing at the keyboard. This is so we can
  894. * do some stuff faster since we will be getting only one or two chars of
  895. * input.
  896. *
  897. * History:
  898. * Created ???
  899. * 4-18-91 Mikehar Win31 Merge
  900. \***************************************************************************/
  901. ICH MLInsertText(
  902. PED ped,
  903. LPSTR lpText,
  904. ICH cchInsert,
  905. BOOL fUserTyping)
  906. {
  907. HDC hdc;
  908. ICH validCch = cchInsert;
  909. ICH oldCaret = ped->ichCaret;
  910. int oldCaretLine = ped->iCaretLine;
  911. BOOL fCRLF = FALSE;
  912. LONG ll, hl;
  913. POINT xyPosInitial;
  914. POINT xyPosFinal;
  915. HWND hwndSave = ped->hwnd;
  916. UNDO undo;
  917. ICH validCchTemp;
  918. xyPosInitial.x=0;
  919. xyPosInitial.y=0;
  920. xyPosFinal.x=0;
  921. xyPosFinal.y=0;
  922. if (validCch == 0)
  923. return 0;
  924. if (ped->cchTextMax <= ped->cch) {
  925. /*
  926. * When the max chars is reached already, notify parent
  927. * Fix for Bug #4183 -- 02/06/91 -- SANKAR --
  928. */
  929. ECNotifyParent(ped,EN_MAXTEXT);
  930. return 0;
  931. }
  932. /*
  933. * Limit the amount of text we add
  934. */
  935. validCch = min(validCch, ped->cchTextMax - ped->cch);
  936. /*
  937. * Make sure we don't split a CRLF in half
  938. */
  939. if (validCch) {
  940. if (ped->fAnsi) {
  941. if (*(WORD UNALIGNED *)(lpText + validCch - 1) == 0x0A0D)
  942. validCch--;
  943. } else {
  944. if (*(DWORD UNALIGNED *)(lpText + (validCch - 1) * ped->cbChar) == 0x000A000D)
  945. validCch--;
  946. }
  947. }
  948. if (!validCch) {
  949. /*
  950. * When the max chars is reached already, notify parent
  951. * Fix for Bug #4183 -- 02/06/91 -- SANKAR --
  952. */
  953. ECNotifyParent(ped,EN_MAXTEXT);
  954. return 0;
  955. }
  956. if (validCch == 2) {
  957. if (ped->fAnsi) {
  958. if (*(WORD UNALIGNED *)lpText == 0x0A0D)
  959. fCRLF = TRUE;
  960. } else {
  961. if (*(DWORD UNALIGNED *)lpText == 0x000A000D)
  962. fCRLF = TRUE;
  963. }
  964. }
  965. //
  966. // Save current undo state always, but clear it out only if !AutoVScroll
  967. //
  968. ECSaveUndo(Pundo(ped), (PUNDO)&undo, !ped->fAutoVScroll);
  969. hdc = ECGetEditDC(ped, FALSE);
  970. /*
  971. * We only need the y position. Since with an LPK loaded
  972. * calculating the x position is an intensive job, just
  973. * call MLIchToYPos.
  974. */
  975. if (ped->cch)
  976. if (ped->pLpkEditCallout)
  977. xyPosInitial.y = MLIchToYPos(ped, ped->cch-1, FALSE);
  978. else
  979. MLIchToXYPos(ped, hdc, ped->cch - 1, FALSE, &xyPosInitial);
  980. /*
  981. * Insert the text
  982. */
  983. validCchTemp = validCch; // may not be needed, but just for precautions..
  984. if (!ECInsertText(ped, lpText, &validCchTemp)) {
  985. // Restore previous undo buffer if it was cleared
  986. if (!ped->fAutoVScroll)
  987. ECSaveUndo((PUNDO)&undo, Pundo(ped), FALSE);
  988. ECReleaseEditDC(ped, hdc, FALSE);
  989. ECNotifyParent(ped, EN_ERRSPACE);
  990. return (0);
  991. }
  992. #if DBG
  993. if (validCch != validCchTemp) {
  994. /*
  995. * All characters in lpText has not been inserted to ped.
  996. * This could happen when cch is close to cchMax.
  997. * Better revisit this after NT5 ships.
  998. */
  999. RIPMSG2(RIP_WARNING, "MLInsertText: validCch is changed (%x -> %x) in ECInsertText.",
  1000. validCch, validCchTemp);
  1001. }
  1002. #endif
  1003. /*
  1004. * Note that ped->ichCaret is updated by ECInsertText
  1005. */
  1006. MLBuildchLines(ped, (ICH)oldCaretLine, (int)validCch, fCRLF?(BOOL)FALSE:fUserTyping, &ll, &hl);
  1007. if (ped->cch)
  1008. /*
  1009. * We only need the y position. Since with an LPK loaded
  1010. * calculating the x position is an intensive job, just
  1011. * call MLIchToYPos.
  1012. */
  1013. if (ped->pLpkEditCallout)
  1014. xyPosFinal.y = MLIchToYPos(ped, ped->cch-1, FALSE);
  1015. else
  1016. MLIchToXYPos(ped, hdc, ped->cch - 1, FALSE,&xyPosFinal);
  1017. if (xyPosFinal.y < xyPosInitial.y && ((ICH)ped->ichScreenStart) + ped->ichLinesOnScreen >= ped->cLines - 1) {
  1018. RECT rc;
  1019. CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt);
  1020. rc.top = xyPosFinal.y + ped->lineHeight;
  1021. if (ped->pLpkEditCallout) {
  1022. int xFarOffset = ped->xOffset + ped->rcFmt.right - ped->rcFmt.left;
  1023. // Include left or right margins in display unless clipped
  1024. // by horizontal scrolling.
  1025. if (ped->wLeftMargin) {
  1026. if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
  1027. && ( (!ped->fRtoLReading && ped->xOffset > 0) // LTR and first char not fully in view
  1028. || ( ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { //RTL and last char not fully in view
  1029. rc.left -= ped->wLeftMargin;
  1030. }
  1031. }
  1032. // Process right margin
  1033. if (ped->wRightMargin) {
  1034. if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
  1035. && ( ( ped->fRtoLReading && ped->xOffset > 0) // RTL and first char not fully in view
  1036. || (!ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { // LTR and last char not fully in view
  1037. rc.right += ped->wRightMargin;
  1038. }
  1039. }
  1040. }
  1041. NtUserInvalidateRect(ped->hwnd, (LPRECT)&rc, TRUE);
  1042. }
  1043. if (!ped->fAutoVScroll) {
  1044. if (ped->ichLinesOnScreen < ped->cLines) {
  1045. MLUndo(ped);
  1046. ECEmptyUndo(Pundo(ped));
  1047. ECSaveUndo(&undo, Pundo(ped), FALSE);
  1048. NtUserMessageBeep(0);
  1049. ECReleaseEditDC(ped, hdc, FALSE);
  1050. /*
  1051. * When the max lines is reached already, notify parent
  1052. * Fix for Bug #7586 -- 10/14/91 -- SANKAR --
  1053. */
  1054. ECNotifyParent(ped,EN_MAXTEXT);
  1055. return (0);
  1056. } else {
  1057. ECEmptyUndo(&undo);
  1058. }
  1059. }
  1060. if (fUserTyping && ped->fWrap) {
  1061. //
  1062. // To avoid oldCaret points intermediate of DBCS character,
  1063. // adjust oldCaret position if necessary.
  1064. //
  1065. // !!!CR If MLBuildchLines() returns reasonable value ( and I think
  1066. // it does), we don't probably need this. Check this out later.
  1067. //
  1068. if (ped->fDBCS && ped->fAnsi) {
  1069. oldCaret = ECAdjustIch(ped,
  1070. ECLock(ped),
  1071. min((ICH)LOWORD(ll),oldCaret));
  1072. /* ECUnlock(ped); */
  1073. } else { // same as original code
  1074. oldCaret = min((ICH)LOWORD(ll), oldCaret);
  1075. }
  1076. }
  1077. // Update ped->iCaretLine properly.
  1078. MLUpdateiCaretLine(ped);
  1079. ECNotifyParent(ped, EN_UPDATE);
  1080. /*
  1081. * Make sure window still exists.
  1082. */
  1083. if (!IsWindow(hwndSave))
  1084. return 0;
  1085. if (_IsWindowVisible(ped->pwnd)) {
  1086. //
  1087. // If the current font has negative A widths, we may have to start
  1088. // drawing a few characters before the oldCaret position.
  1089. //
  1090. if (ped->wMaxNegAcharPos) {
  1091. int iLine = MLIchToLine(ped, oldCaret);
  1092. oldCaret = max( ((int)(oldCaret - ped->wMaxNegAcharPos)),
  1093. ((int)(ped->chLines[iLine])));
  1094. }
  1095. // Redraw to end of screen/text if CRLF or large insert
  1096. if (fCRLF || !fUserTyping) {
  1097. /*
  1098. * Redraw to end of screen/text if crlf or large insert.
  1099. */
  1100. MLDrawText(ped, hdc, (fUserTyping ? oldCaret : 0), ped->cch, FALSE);
  1101. } else
  1102. MLDrawText(ped, hdc, oldCaret, max(ped->ichCaret, (ICH)hl), FALSE);
  1103. }
  1104. ECReleaseEditDC(ped, hdc, FALSE);
  1105. /*
  1106. * Make sure we can see the cursor
  1107. */
  1108. MLEnsureCaretVisible(ped);
  1109. ped->fDirty = TRUE;
  1110. ECNotifyParent(ped, EN_CHANGE);
  1111. if (validCch < cchInsert)
  1112. ECNotifyParent(ped, EN_MAXTEXT);
  1113. if (validCch) {
  1114. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  1115. }
  1116. /*
  1117. * Make sure the window still exists.
  1118. */
  1119. if (!IsWindow(hwndSave))
  1120. return 0;
  1121. else
  1122. return validCch;
  1123. }
  1124. /***************************************************************************\
  1125. *
  1126. * MLReplaceSel() -
  1127. *
  1128. * Replaces currently selected text with the passed in text, WITH UNDO
  1129. * CAPABILITIES.
  1130. *
  1131. \***************************************************************************/
  1132. void MLReplaceSel(PED ped, LPSTR lpText)
  1133. {
  1134. ICH cchText;
  1135. //
  1136. // Delete text, which will put it into the clean undo buffer.
  1137. //
  1138. ECEmptyUndo(Pundo(ped));
  1139. MLDeleteText(ped);
  1140. //
  1141. // B#3356
  1142. // Some apps do "clear" by selecting all of the text, then replacing
  1143. // it with "", in which case MLInsertText() will return 0. But that
  1144. // doesn't mean failure...
  1145. //
  1146. if ( ped->fAnsi )
  1147. cchText = strlen(lpText);
  1148. else
  1149. cchText = wcslen((LPWSTR)lpText);
  1150. if (cchText ) {
  1151. BOOL fFailed;
  1152. UNDO undo;
  1153. HWND hwndSave;
  1154. //
  1155. // B#1385,1427
  1156. // Save undo buffer, but DO NOT CLEAR IT. We want to restore it
  1157. // if insertion fails due to OOM.
  1158. //
  1159. ECSaveUndo(Pundo(ped), (PUNDO)&undo, FALSE);
  1160. hwndSave = ped->hwnd;
  1161. fFailed = (BOOL) !MLInsertText(ped, lpText, cchText, FALSE);
  1162. if (!IsWindow(hwndSave))
  1163. return;
  1164. if (fFailed) {
  1165. //
  1166. // UNDO the previous edit
  1167. //
  1168. ECSaveUndo((PUNDO)&undo, Pundo(ped), FALSE);
  1169. MLUndo(ped);
  1170. }
  1171. }
  1172. }
  1173. /***************************************************************************\
  1174. * MLDeleteText AorW
  1175. *
  1176. * Deletes the characters between ichMin and ichMax. Returns the
  1177. * number of characters we deleted.
  1178. *
  1179. * History:
  1180. \***************************************************************************/
  1181. ICH MLDeleteText(
  1182. PED ped)
  1183. {
  1184. ICH minSel = ped->ichMinSel;
  1185. ICH maxSel = ped->ichMaxSel;
  1186. ICH cchDelete;
  1187. HDC hdc;
  1188. int minSelLine;
  1189. int maxSelLine;
  1190. POINT xyPos;
  1191. RECT rc;
  1192. BOOL fFastDelete = FALSE;
  1193. LONG hl;
  1194. INT cchcount = 0;
  1195. /*
  1196. * Get what line the min selection is on so that we can start rebuilding the
  1197. * text from there if we delete anything.
  1198. */
  1199. minSelLine = MLIchToLine(ped, minSel);
  1200. maxSelLine = MLIchToLine(ped, maxSel);
  1201. //
  1202. // Calculate fFastDelete and cchcount
  1203. //
  1204. if (ped->fAnsi && ped->fDBCS) {
  1205. if ((ped->fAutoVScroll) &&
  1206. (minSelLine == maxSelLine) &&
  1207. (ped->chLines[minSelLine] != minSel) &&
  1208. (ECNextIch(ped,NULL,minSel) == maxSel)) {
  1209. fFastDelete = TRUE;
  1210. cchcount = ((maxSel - minSel) == 1) ? 0 : -1;
  1211. }
  1212. } else if (((maxSel - minSel) == 1) && (minSelLine == maxSelLine) && (ped->chLines[minSelLine] != minSel)) {
  1213. if (!ped->fAutoVScroll)
  1214. fFastDelete = FALSE;
  1215. else
  1216. fFastDelete = TRUE;
  1217. }
  1218. if (!(cchDelete = ECDeleteText(ped)))
  1219. return (0);
  1220. /*
  1221. * Start building lines at minsel line since caretline may be at the max sel
  1222. * point.
  1223. */
  1224. if (fFastDelete) {
  1225. //
  1226. // cchcount is (-1) if it's a double byte character
  1227. //
  1228. MLShiftchLines(ped, minSelLine + 1, -2 + cchcount);
  1229. MLBuildchLines(ped, minSelLine, 1, TRUE, NULL, &hl);
  1230. } else {
  1231. MLBuildchLines(ped, max(minSelLine-1,0), -(int)cchDelete, FALSE, NULL, NULL);
  1232. }
  1233. MLUpdateiCaretLine(ped);
  1234. ECNotifyParent(ped, EN_UPDATE);
  1235. if (_IsWindowVisible(ped->pwnd)) {
  1236. /*
  1237. * Now update the screen to reflect the deletion
  1238. */
  1239. hdc = ECGetEditDC(ped, FALSE);
  1240. /*
  1241. * Otherwise just redraw starting at the line we just entered
  1242. */
  1243. minSelLine = max(minSelLine-1,0);
  1244. MLDrawText(ped, hdc, ped->chLines[minSelLine],
  1245. fFastDelete ? hl : ped->cch, FALSE);
  1246. CopyRect(&rc, &ped->rcFmt);
  1247. rc.left -= ped->wLeftMargin;
  1248. rc.right += ped->wRightMargin;
  1249. if (ped->cch) {
  1250. /*
  1251. * Clear from end of text to end of window.
  1252. *
  1253. * We only need the y position. Since with an LPK loaded
  1254. * calculating the x position is an intensive job, just
  1255. * call MLIchToYPos.
  1256. */
  1257. if (ped->pLpkEditCallout)
  1258. xyPos.y = MLIchToYPos(ped, ped->cch, FALSE);
  1259. else
  1260. MLIchToXYPos(ped, hdc, ped->cch, FALSE, &xyPos);
  1261. rc.top = xyPos.y + ped->lineHeight;
  1262. }
  1263. NtUserInvalidateRect(ped->hwnd, &rc, TRUE);
  1264. ECReleaseEditDC(ped, hdc, FALSE);
  1265. MLEnsureCaretVisible(ped);
  1266. }
  1267. ped->fDirty = TRUE;
  1268. ECNotifyParent(ped, EN_CHANGE);
  1269. if (cchDelete)
  1270. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  1271. return cchDelete;
  1272. }
  1273. /***************************************************************************\
  1274. * MLInsertchLine AorW
  1275. *
  1276. * Inserts the line iline and sets its starting character index to be
  1277. * ich. All the other line indices are moved up. Returns TRUE if successful
  1278. * else FALSE and notifies the parent that there was no memory.
  1279. *
  1280. * History:
  1281. \***************************************************************************/
  1282. BOOL MLInsertchLine(
  1283. PED ped,
  1284. ICH iLine,
  1285. ICH ich,
  1286. BOOL fUserTyping)
  1287. {
  1288. DWORD dwSize;
  1289. if (fUserTyping && iLine < ped->cLines) {
  1290. ped->chLines[iLine] = ich;
  1291. return (TRUE);
  1292. }
  1293. dwSize = (ped->cLines + 2) * sizeof(int);
  1294. if (dwSize > UserLocalSize(ped->chLines)) {
  1295. LPICH hResult;
  1296. /*
  1297. * Grow the line index buffer
  1298. */
  1299. dwSize += LINEBUMP * sizeof(int);
  1300. hResult = (LPICH)UserLocalReAlloc(ped->chLines, dwSize, 0);
  1301. if (!hResult) {
  1302. ECNotifyParent(ped, EN_ERRSPACE);
  1303. return FALSE;
  1304. }
  1305. ped->chLines = hResult;
  1306. }
  1307. /*
  1308. * Move indices starting at iLine up
  1309. */
  1310. if (ped->cLines != iLine)
  1311. RtlMoveMemory(&ped->chLines[iLine + 1], &ped->chLines[iLine],
  1312. (ped->cLines - iLine) * sizeof(int));
  1313. ped->cLines++;
  1314. ped->chLines[iLine] = ich;
  1315. return TRUE;
  1316. }
  1317. /***************************************************************************\
  1318. * MLShiftchLines AorW
  1319. *
  1320. * Move the starting index of all lines iLine or greater by delta
  1321. * bytes.
  1322. *
  1323. * History:
  1324. \***************************************************************************/
  1325. void MLShiftchLines(
  1326. PED ped,
  1327. ICH iLine,
  1328. int delta)
  1329. {
  1330. if (iLine >= ped->cLines)
  1331. return;
  1332. /*
  1333. * Just add delta to the starting point of each line after iLine
  1334. */
  1335. for (; iLine < ped->cLines; iLine++)
  1336. ped->chLines[iLine] += delta;
  1337. }
  1338. /***************************************************************************\
  1339. * MLBuildchLines AorW
  1340. *
  1341. * Rebuilds the start of line array (ped->chLines) starting at line
  1342. * number ichLine.
  1343. *
  1344. * History:
  1345. \***************************************************************************/
  1346. void MLBuildchLines(
  1347. PED ped,
  1348. ICH iLine,
  1349. int cchDelta, // Number of chars added or deleted
  1350. BOOL fUserTyping,
  1351. PLONG pll,
  1352. PLONG phl)
  1353. {
  1354. PSTR ptext; /* Starting address of the text */
  1355. /*
  1356. * We keep these ICH's so that we can Unlock ped->hText when we have to grow
  1357. * the chlines array. With large text handles, it becomes a problem if we
  1358. * have a locked block in the way.
  1359. */
  1360. ICH ichLineStart;
  1361. ICH ichLineEnd;
  1362. ICH ichLineEndBeforeCRLF;
  1363. ICH ichCRLF;
  1364. ICH cch;
  1365. HDC hdc;
  1366. BOOL fLineBroken = FALSE; /* Initially, no new line breaks are made */
  1367. ICH minCchBreak;
  1368. ICH maxCchBreak;
  1369. BOOL fOnDelimiter;
  1370. if (!ped->cch) {
  1371. ped->maxPixelWidth = 0;
  1372. ped->xOffset = 0;
  1373. ped->ichScreenStart = 0;
  1374. ped->cLines = 1;
  1375. if (pll)
  1376. *pll = 0;
  1377. if (phl)
  1378. *phl = 0;
  1379. goto UpdateScroll;
  1380. }
  1381. if (fUserTyping && cchDelta)
  1382. MLShiftchLines(ped, iLine + 1, cchDelta);
  1383. hdc = ECGetEditDC(ped, TRUE);
  1384. if (!iLine && !cchDelta && !fUserTyping) {
  1385. /*
  1386. * Reset maxpixelwidth only if we will be running through the whole
  1387. * text. Better too long than too short.
  1388. */
  1389. ped->maxPixelWidth = 0;
  1390. /*
  1391. * Reset number of lines in text since we will be running through all
  1392. * the text anyway...
  1393. */
  1394. ped->cLines = 1;
  1395. }
  1396. /*
  1397. * Set min and max line built to be the starting line
  1398. */
  1399. minCchBreak = maxCchBreak = (cchDelta ? ped->chLines[iLine] : 0);
  1400. ptext = ECLock(ped);
  1401. ichCRLF = ichLineStart = ped->chLines[iLine];
  1402. while (ichLineStart < ped->cch) {
  1403. if (ichLineStart >= ichCRLF) {
  1404. ichCRLF = ichLineStart;
  1405. /*
  1406. * Move ichCRLF ahead to either the first CR or to the end of text.
  1407. */
  1408. if (ped->fAnsi) {
  1409. while (ichCRLF < ped->cch) {
  1410. if (*(ptext + ichCRLF) == 0x0D) {
  1411. if (*(ptext + ichCRLF + 1) == 0x0A ||
  1412. *(WORD UNALIGNED *)(ptext + ichCRLF + 1) == 0x0A0D)
  1413. break;
  1414. }
  1415. ichCRLF++;
  1416. }
  1417. } else {
  1418. LPWSTR pwtext = (LPWSTR)ptext;
  1419. while (ichCRLF < ped->cch) {
  1420. if (*(pwtext + ichCRLF) == 0x0D) {
  1421. if (*(pwtext + ichCRLF + 1) == 0x0A ||
  1422. *(DWORD UNALIGNED *)(pwtext + ichCRLF + 1) == 0x000A000D)
  1423. break;
  1424. }
  1425. ichCRLF++;
  1426. }
  1427. }
  1428. }
  1429. if (!ped->fWrap) {
  1430. UINT LineWidth;
  1431. /*
  1432. * If we are not word wrapping, line breaks are signified by CRLF.
  1433. */
  1434. //
  1435. // If we cut off the line at MAXLINELENGTH, we should
  1436. // adjust ichLineEnd.
  1437. //
  1438. if ((ichCRLF - ichLineStart) <= MAXLINELENGTH) {
  1439. ichLineEnd = ichCRLF;
  1440. } else {
  1441. ichLineEnd = ichLineStart + MAXLINELENGTH;
  1442. if (ped->fAnsi && ped->fDBCS) {
  1443. ichLineEnd = ECAdjustIch( ped, (PSTR)ptext, ichLineEnd);
  1444. }
  1445. }
  1446. /*
  1447. * We will keep track of what the longest line is for the horizontal
  1448. * scroll bar thumb positioning.
  1449. */
  1450. if (ped->pLpkEditCallout) {
  1451. LineWidth = ped->pLpkEditCallout->EditGetLineWidth(
  1452. ped, hdc, ptext + ichLineStart*ped->cbChar,
  1453. ichLineEnd - ichLineStart);
  1454. } else {
  1455. LineWidth = MLGetLineWidth(hdc, ptext + ichLineStart * ped->cbChar,
  1456. ichLineEnd - ichLineStart,
  1457. ped);
  1458. }
  1459. ped->maxPixelWidth = max(ped->maxPixelWidth,(int)LineWidth);
  1460. } else {
  1461. /*
  1462. * Check if the width of the edit control is non-zero;
  1463. * a part of the fix for Bug #7402 -- SANKAR -- 01/21/91 --
  1464. */
  1465. if(ped->rcFmt.right > ped->rcFmt.left) {
  1466. /*
  1467. * Find the end of the line based solely on text extents
  1468. */
  1469. if (ped->pLpkEditCallout) {
  1470. ichLineEnd = ichLineStart +
  1471. ped->pLpkEditCallout->EditCchInWidth(
  1472. ped, hdc, ptext + ped->cbChar*ichLineStart,
  1473. ichCRLF - ichLineStart,
  1474. ped->rcFmt.right - ped->rcFmt.left);
  1475. } else {
  1476. if (ped->fAnsi) {
  1477. ichLineEnd = ichLineStart +
  1478. ECCchInWidth(ped, hdc,
  1479. ptext + ichLineStart,
  1480. ichCRLF - ichLineStart,
  1481. ped->rcFmt.right - ped->rcFmt.left,
  1482. TRUE);
  1483. } else {
  1484. ichLineEnd = ichLineStart +
  1485. ECCchInWidth(ped, hdc,
  1486. (LPSTR)((LPWSTR)ptext + ichLineStart),
  1487. ichCRLF - ichLineStart,
  1488. ped->rcFmt.right - ped->rcFmt.left,
  1489. TRUE);
  1490. }
  1491. }
  1492. } else {
  1493. ichLineEnd = ichLineStart;
  1494. }
  1495. if (ichLineEnd == ichLineStart && ichCRLF - ichLineStart) {
  1496. /*
  1497. * Maintain a minimum of one char per line
  1498. */
  1499. //
  1500. // Since it might be a double byte char, so calling ECNextIch.
  1501. //
  1502. ichLineEnd = ECNextIch(ped, NULL, ichLineEnd);
  1503. }
  1504. /*
  1505. * Now starting from ichLineEnd, if we are not at a hard line break,
  1506. * then if we are not at a space AND the char before us is
  1507. * not a space,(OR if we are at a CR) we will look word left for the
  1508. * start of the word to break at.
  1509. * This change was done for TWO reasons:
  1510. * 1. If we are on a delimiter, no need to look word left to break at.
  1511. * 2. If the previous char is a delimter, we can break at current char.
  1512. * Change done by -- SANKAR --01/31/91--
  1513. */
  1514. if (ichLineEnd != ichCRLF) {
  1515. if(ped->lpfnNextWord) {
  1516. fOnDelimiter = (CALLWORDBREAKPROC(*ped->lpfnNextWord, ptext,
  1517. ichLineEnd, ped->cch, WB_ISDELIMITER) ||
  1518. CALLWORDBREAKPROC(*ped->lpfnNextWord, ptext, ichLineEnd - 1,
  1519. ped->cch, WB_ISDELIMITER));
  1520. //
  1521. // This change was done for FOUR reasons:
  1522. //
  1523. // 1. If we are on a delimiter, no need to look word left to break at.
  1524. // 2. If we are on a double byte character, we can break at current char.
  1525. // 3. If the previous char is a delimter, we can break at current char.
  1526. // 4. If the previous char is a double byte character, we can break at current char.
  1527. //
  1528. } else if (ped->fAnsi) {
  1529. fOnDelimiter = (ISDELIMETERA(*(ptext + ichLineEnd)) ||
  1530. ECIsDBCSLeadByte(ped, *(ptext + ichLineEnd)));
  1531. if (!fOnDelimiter) {
  1532. PSTR pPrev = ECAnsiPrev(ped,ptext,ptext+ichLineEnd);
  1533. fOnDelimiter = ISDELIMETERA(*pPrev) ||
  1534. ECIsDBCSLeadByte(ped,*pPrev);
  1535. }
  1536. } else { // Unicode
  1537. fOnDelimiter = (ISDELIMETERW(*((LPWSTR)ptext + ichLineEnd)) ||
  1538. UserIsFullWidth(CP_ACP,*((LPWSTR)ptext + ichLineEnd)) ||
  1539. ISDELIMETERW(*((LPWSTR)ptext + ichLineEnd - 1)) ||
  1540. UserIsFullWidth(CP_ACP,*((LPWSTR)ptext + ichLineEnd - 1)));
  1541. }
  1542. if (!fOnDelimiter ||
  1543. (ped->fAnsi && *(ptext + ichLineEnd) == 0x0D) ||
  1544. (!ped->fAnsi && *((LPWSTR)ptext + ichLineEnd) == 0x0D)) {
  1545. if (ped->lpfnNextWord != NULL) {
  1546. cch = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)ptext, ichLineEnd,
  1547. ped->cch, WB_LEFT);
  1548. } else {
  1549. ped->fCalcLines = TRUE;
  1550. ECWord(ped, ichLineEnd, TRUE, &cch, NULL);
  1551. ped->fCalcLines = FALSE;
  1552. }
  1553. if (cch > ichLineStart) {
  1554. ichLineEnd = cch;
  1555. }
  1556. /*
  1557. * Now, if the above test fails, it means the word left goes
  1558. * back before the start of the line ie. a word is longer
  1559. * than a line on the screen. So, we just fit as much of
  1560. * the word on the line as possible. Thus, we use the
  1561. * pLineEnd we calculated solely on width at the beginning
  1562. * of this else block...
  1563. */
  1564. }
  1565. }
  1566. }
  1567. #if 0
  1568. if (!ISDELIMETERAW((*(ptext + (ichLineEnd - 1)*ped->cbChar))) && ISDELIMETERAW((*(ptext + ichLineEnd*ped->cbChar)))) #ERROR
  1569. if ((*(ptext + ichLineEnd - 1) != ' ' &&
  1570. *(ptext + ichLineEnd - 1) != VK_TAB) &&
  1571. (*(ptext + ichLineEnd) == ' ' ||
  1572. *(ptext + ichLineEnd) == VK_TAB))
  1573. #endif
  1574. if (AWCOMPARECHAR(ped,ptext + ichLineEnd * ped->cbChar, ' ') ||
  1575. AWCOMPARECHAR(ped,ptext + ichLineEnd * ped->cbChar, VK_TAB)) {
  1576. /*
  1577. * Swallow the space at the end of a line.
  1578. */
  1579. if (ichLineEnd < ped->cch) {
  1580. ichLineEnd++;
  1581. }
  1582. }
  1583. /*
  1584. * Skip over crlf or crcrlf if it exists. Thus, ichLineEnd is the first
  1585. * character in the next line.
  1586. */
  1587. ichLineEndBeforeCRLF = ichLineEnd;
  1588. if (ped->fAnsi) {
  1589. if (ichLineEnd < ped->cch && *(ptext + ichLineEnd) == 0x0D)
  1590. ichLineEnd += (ichLineEnd + 1 == ped->cch) ? 1 : 2;
  1591. /*
  1592. * Skip over CRCRLF
  1593. */
  1594. if (ichLineEnd < ped->cch && *(ptext + ichLineEnd) == 0x0A)
  1595. ichLineEnd++;
  1596. UserAssert(ichLineEnd <= ped->cch);
  1597. } else {
  1598. if (ichLineEnd < ped->cch && *(((LPWSTR)ptext) + ichLineEnd) == 0x0D)
  1599. ichLineEnd += (ichLineEnd + 1 == ped->cch) ? 1 : 2;
  1600. /*
  1601. * Skip over CRCRLF
  1602. */
  1603. if (ichLineEnd < ped->cch && *(((LPWSTR)ptext) + ichLineEnd) == 0x0A) {
  1604. ichLineEnd++;
  1605. RIPMSG0(RIP_VERBOSE, "Skip over CRCRLF\n");
  1606. }
  1607. UserAssert(ichLineEnd <= ped->cch);
  1608. }
  1609. /*
  1610. * Now, increment iLine, allocate space for the next line, and set its
  1611. * starting point
  1612. */
  1613. iLine++;
  1614. if (!fUserTyping || (iLine > ped->cLines - 1) || (ped->chLines[iLine] != ichLineEnd)) {
  1615. /*
  1616. * The line break occured in a different place than before.
  1617. */
  1618. if (!fLineBroken) {
  1619. /*
  1620. * Since we haven't broken a line before, just set the min
  1621. * break line.
  1622. */
  1623. fLineBroken = TRUE;
  1624. if (ichLineEndBeforeCRLF == ichLineEnd)
  1625. minCchBreak = maxCchBreak = (ichLineEnd ? ichLineEnd - 1 : 0);
  1626. else
  1627. minCchBreak = maxCchBreak = ichLineEndBeforeCRLF;
  1628. }
  1629. maxCchBreak = max(maxCchBreak, ichLineEnd);
  1630. ECUnlock(ped);
  1631. /*
  1632. * Now insert the new line into the array
  1633. */
  1634. if (!MLInsertchLine(ped, iLine, ichLineEnd, (BOOL)(cchDelta != 0)))
  1635. goto EndUp;
  1636. ptext = ECLock(ped);
  1637. } else {
  1638. maxCchBreak = ped->chLines[iLine];
  1639. /*
  1640. * Quick escape
  1641. */
  1642. goto UnlockAndEndUp;
  1643. }
  1644. ichLineStart = ichLineEnd;
  1645. } /* end while (ichLineStart < ped->cch) */
  1646. if (iLine != ped->cLines) {
  1647. RIPMSG1(RIP_VERBOSE, "chLines[%d] is being cleared.\n", iLine);
  1648. ped->cLines = iLine;
  1649. ped->chLines[ped->cLines] = 0;
  1650. }
  1651. /*
  1652. * Note that we incremented iLine towards the end of the while loop so, the
  1653. * index, iLine, is actually equal to the line count
  1654. */
  1655. if (ped->cch && AWCOMPARECHAR(ped, ptext + (ped->cch - 1)*ped->cbChar, 0x0A) &&
  1656. ped->chLines[ped->cLines - 1] < ped->cch) {
  1657. /*
  1658. * Make sure last line has no crlf in it
  1659. */
  1660. if (!fLineBroken) {
  1661. /*
  1662. * Since we haven't broken a line before, just set the min break
  1663. * line.
  1664. */
  1665. fLineBroken = TRUE;
  1666. minCchBreak = ped->cch - 1;
  1667. }
  1668. maxCchBreak = max(maxCchBreak, ichLineEnd);
  1669. ECUnlock(ped);
  1670. MLInsertchLine(ped, iLine, ped->cch, FALSE);
  1671. MLSanityCheck(ped);
  1672. } else
  1673. UnlockAndEndUp:
  1674. ECUnlock(ped);
  1675. EndUp:
  1676. ECReleaseEditDC(ped, hdc, TRUE);
  1677. if (pll)
  1678. *pll = minCchBreak;
  1679. if (phl)
  1680. *phl = maxCchBreak;
  1681. UpdateScroll:
  1682. MLScroll(ped, FALSE, ML_REFRESH, 0, TRUE);
  1683. MLScroll(ped, TRUE, ML_REFRESH, 0, TRUE);
  1684. MLSanityCheck(ped);
  1685. return;
  1686. }
  1687. /***************************************************************************\
  1688. *
  1689. * MLPaint()
  1690. *
  1691. * Response to WM_PAINT message.
  1692. *
  1693. \***************************************************************************/
  1694. void MLPaint(PED ped, HDC hdc, LPRECT lprc)
  1695. {
  1696. HFONT hOldFont;
  1697. ICH imin;
  1698. ICH imax;
  1699. //
  1700. // Do we need to draw the border ourself for old apps?
  1701. //
  1702. if (ped->fFlatBorder)
  1703. {
  1704. RECT rcT;
  1705. _GetClientRect(ped->pwnd, &rcT);
  1706. if (TestWF(ped->pwnd, WFSIZEBOX))
  1707. {
  1708. InflateRect(&rcT, SYSMET(CXBORDER) - SYSMET(CXFRAME),
  1709. SYSMET(CYBORDER) - SYSMET(CYFRAME));
  1710. }
  1711. DrawFrame(hdc, &rcT, 1, DF_WINDOWFRAME);
  1712. }
  1713. ECSetEditClip(ped, hdc, (BOOL) (ped->xOffset == 0));
  1714. if (ped->hFont)
  1715. hOldFont = SelectObject(hdc, ped->hFont);
  1716. if (!lprc) {
  1717. // no partial rect given -- draw all text
  1718. imin = 0;
  1719. imax = ped->cch;
  1720. } else {
  1721. // only draw pertinent text
  1722. imin = (ICH) MLMouseToIch(ped, hdc, ((LPPOINT) &lprc->left), NULL) - 1;
  1723. if (imin == -1)
  1724. imin = 0;
  1725. // HACK_ALERT:
  1726. // The 3 is required here because, MLMouseToIch() returns decremented
  1727. // value; We must fix MLMouseToIch.
  1728. imax = (ICH) MLMouseToIch(ped, hdc, ((LPPOINT) &lprc->right), NULL) + 3;
  1729. if (imax > ped->cch)
  1730. imax = ped->cch;
  1731. }
  1732. MLDrawText(ped, hdc, imin, imax, FALSE);
  1733. if (ped->hFont)
  1734. SelectObject(hdc, hOldFont);
  1735. }
  1736. /***************************************************************************\
  1737. * MLKeyDown AorW
  1738. *
  1739. * Handles cursor movement and other VIRT KEY stuff. keyMods allows
  1740. * us to make MLKeyDownHandler calls and specify if the modifier keys (shift
  1741. * and control) are up or down. If keyMods == 0, we get the keyboard state
  1742. * using GetKeyState(VK_SHIFT) etc. Otherwise, the bits in keyMods define the
  1743. * state of the shift and control keys.
  1744. *
  1745. * History:
  1746. \***************************************************************************/
  1747. void MLKeyDown(
  1748. PED ped,
  1749. UINT virtKeyCode,
  1750. int keyMods)
  1751. {
  1752. HDC hdc;
  1753. BOOL prevLine;
  1754. POINT mousePt;
  1755. int defaultDlgId;
  1756. int iScrollAmt;
  1757. /*
  1758. * Variables we will use for redrawing the updated text
  1759. */
  1760. /*
  1761. * new selection is specified by newMinSel, newMaxSel
  1762. */
  1763. ICH newMaxSel = ped->ichMaxSel;
  1764. ICH newMinSel = ped->ichMinSel;
  1765. /*
  1766. * Flags for drawing the updated text
  1767. */
  1768. BOOL changeSelection = FALSE;
  1769. /*
  1770. * Comparisons we do often
  1771. */
  1772. BOOL MinEqMax = (newMaxSel == newMinSel);
  1773. BOOL MinEqCar = (ped->ichCaret == newMinSel);
  1774. BOOL MaxEqCar = (ped->ichCaret == newMaxSel);
  1775. /*
  1776. * State of shift and control keys.
  1777. */
  1778. int scState;
  1779. if (ped->fMouseDown) {
  1780. /*
  1781. * If we are in the middle of a mousedown command, don't do anything.
  1782. */
  1783. return ;
  1784. }
  1785. scState = ECGetModKeys(keyMods);
  1786. switch (virtKeyCode) {
  1787. case VK_ESCAPE:
  1788. if (ped->fInDialogBox) {
  1789. /*
  1790. * This condition is removed because, if the dialogbox does not
  1791. * have a CANCEL button and if ESC is hit when focus is on a
  1792. * ML edit control the dialogbox must close whether it has cancel
  1793. * button or not to be consistent with SL edit control;
  1794. * DefDlgProc takes care of the disabled CANCEL button case.
  1795. * Fix for Bug #4123 -- 02/07/91 -- SANKAR --
  1796. */
  1797. #if 0
  1798. if (GetDlgItem(ped->hwndParent, IDCANCEL))
  1799. #endif
  1800. /*
  1801. * User hit ESC...Send a close message (which in turn sends a
  1802. * cancelID to the app in DefDialogProc...
  1803. */
  1804. PostMessage(ped->hwndParent, WM_CLOSE, 0, 0L);
  1805. }
  1806. return ;
  1807. case VK_RETURN:
  1808. if (ped->fInDialogBox) {
  1809. /*
  1810. * If this multiline edit control is in a dialog box, then we want
  1811. * the RETURN key to be sent to the default dialog button (if there
  1812. * is one). CTRL-RETURN will insert a RETURN into the text. Note
  1813. * that CTRL-RETURN automatically translates into a linefeed (0x0A)
  1814. * and in the MLCharHandler, we handle this as if a return was
  1815. * entered.
  1816. */
  1817. if (scState != CTRLDOWN) {
  1818. if (TestWF(ped->pwnd, EFWANTRETURN)) {
  1819. /*
  1820. * This edit control wants cr to be inserted so break out of
  1821. * case.
  1822. */
  1823. return ;
  1824. }
  1825. defaultDlgId = (int)(DWORD)LOWORD(SendMessage(ped->hwndParent,
  1826. DM_GETDEFID, 0, 0L));
  1827. if (defaultDlgId) {
  1828. HWND hwnd = GetDlgItem(ped->hwndParent, defaultDlgId);
  1829. if (hwnd) {
  1830. SendMessage(ped->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwnd, 1L);
  1831. if (!ped->fFocus)
  1832. PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0L);
  1833. }
  1834. }
  1835. }
  1836. return ;
  1837. }
  1838. break;
  1839. case VK_TAB:
  1840. /*
  1841. * If this multiline edit control is in a dialog box, then we want the
  1842. * TAB key to take you to the next control, shift TAB to take you to the
  1843. * previous control. We always want CTRL-TAB to insert a tab into the
  1844. * edit control regardless of weather or not we're in a dialog box.
  1845. */
  1846. if (scState == CTRLDOWN)
  1847. MLChar(ped, virtKeyCode, keyMods);
  1848. else if (ped->fInDialogBox)
  1849. SendMessage(ped->hwndParent, WM_NEXTDLGCTL, scState == SHFTDOWN, 0L);
  1850. return ;
  1851. case VK_LEFT:
  1852. //
  1853. // If the caret isn't at the beginning, we can move left
  1854. //
  1855. if (ped->ichCaret) {
  1856. // Get new caret pos.
  1857. if (scState & CTRLDOWN) {
  1858. // Move caret word left
  1859. ECWord(ped, ped->ichCaret, TRUE, &ped->ichCaret, NULL);
  1860. } else {
  1861. if (ped->pLpkEditCallout) {
  1862. ped->ichCaret = MLMoveSelectionRestricted(ped, ped->ichCaret, TRUE);
  1863. } else {
  1864. // Move caret char left
  1865. ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, TRUE);
  1866. }
  1867. }
  1868. // Get new selection
  1869. if (scState & SHFTDOWN) {
  1870. if (MaxEqCar && !MinEqMax) {
  1871. // Reduce selection
  1872. newMaxSel = ped->ichCaret;
  1873. UserAssert(newMinSel == ped->ichMinSel);
  1874. } else {
  1875. // Extend selection
  1876. newMinSel = ped->ichCaret;
  1877. }
  1878. } else {
  1879. // Clear selection
  1880. newMaxSel = newMinSel = ped->ichCaret;
  1881. }
  1882. changeSelection = TRUE;
  1883. } else {
  1884. //
  1885. // If the user tries to move left and we are at the 0th
  1886. // character and there is a selection, then cancel the
  1887. // selection.
  1888. //
  1889. if ( (ped->ichMaxSel != ped->ichMinSel) &&
  1890. !(scState & SHFTDOWN) ) {
  1891. changeSelection = TRUE;
  1892. newMaxSel = newMinSel = ped->ichCaret;
  1893. }
  1894. }
  1895. break;
  1896. case VK_RIGHT:
  1897. //
  1898. // If the caret isn't at the end, we can move right.
  1899. //
  1900. if (ped->ichCaret < ped->cch) {
  1901. //
  1902. // Get new caret pos.
  1903. //
  1904. if (scState & CTRLDOWN) {
  1905. // Move caret word right
  1906. ECWord(ped, ped->ichCaret, FALSE, NULL, &ped->ichCaret);
  1907. } else {
  1908. // Move caret char right
  1909. if (ped->pLpkEditCallout) {
  1910. ped->ichCaret = MLMoveSelectionRestricted(ped, ped->ichCaret, FALSE);
  1911. } else {
  1912. ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, FALSE);
  1913. }
  1914. }
  1915. //
  1916. // Get new selection.
  1917. //
  1918. if (scState & SHFTDOWN) {
  1919. if (MinEqCar && !MinEqMax) {
  1920. // Reduce selection
  1921. newMinSel = ped->ichCaret;
  1922. UserAssert(newMaxSel == ped->ichMaxSel);
  1923. } else {
  1924. // Extend selection
  1925. newMaxSel = ped->ichCaret;
  1926. }
  1927. } else {
  1928. // Clear selection
  1929. newMaxSel = newMinSel = ped->ichCaret;
  1930. }
  1931. changeSelection = TRUE;
  1932. } else {
  1933. //
  1934. // If the user tries to move right and we are at the last
  1935. // character and there is a selection, then cancel the
  1936. // selection.
  1937. //
  1938. if ( (ped->ichMaxSel != ped->ichMinSel) &&
  1939. !(scState & SHFTDOWN) ) {
  1940. newMaxSel = newMinSel = ped->ichCaret;
  1941. changeSelection = TRUE;
  1942. }
  1943. }
  1944. break;
  1945. case VK_UP:
  1946. case VK_DOWN:
  1947. if (ped->cLines - 1 != ped->iCaretLine &&
  1948. ped->ichCaret == ped->chLines[ped->iCaretLine + 1])
  1949. prevLine = TRUE;
  1950. else
  1951. prevLine = FALSE;
  1952. hdc = ECGetEditDC(ped, TRUE);
  1953. MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine, &mousePt);
  1954. ECReleaseEditDC(ped, hdc, TRUE);
  1955. mousePt.y += 1 + (virtKeyCode == VK_UP ? -ped->lineHeight : ped->lineHeight);
  1956. if (!(scState & CTRLDOWN)) {
  1957. //
  1958. // Send fake mouse messages to handle this
  1959. // If VK_SHIFT is down, extend selection & move caret up/down
  1960. // 1 line. Otherwise, clear selection & move caret.
  1961. //
  1962. MLMouseMotion(ped, WM_LBUTTONDOWN,
  1963. !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
  1964. MLMouseMotion(ped, WM_LBUTTONUP,
  1965. !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
  1966. }
  1967. break;
  1968. case VK_HOME:
  1969. //
  1970. // Update caret.
  1971. //
  1972. if (scState & CTRLDOWN) {
  1973. // Move caret to beginning of text.
  1974. ped->ichCaret = 0;
  1975. } else {
  1976. // Move caret to beginning of line.
  1977. ped->ichCaret = ped->chLines[ped->iCaretLine];
  1978. }
  1979. //
  1980. // Update selection.
  1981. //
  1982. newMinSel = ped->ichCaret;
  1983. if (scState & SHFTDOWN) {
  1984. if (MaxEqCar && !MinEqMax) {
  1985. if (scState & CTRLDOWN)
  1986. newMaxSel = ped->ichMinSel;
  1987. else {
  1988. newMinSel = ped->ichMinSel;
  1989. newMaxSel = ped->ichCaret;
  1990. }
  1991. }
  1992. } else {
  1993. // Clear selection
  1994. newMaxSel = ped->ichCaret;
  1995. }
  1996. changeSelection = TRUE;
  1997. break;
  1998. case VK_END:
  1999. //
  2000. // Update caret.
  2001. //
  2002. if (scState & CTRLDOWN) {
  2003. // Move caret to end of text.
  2004. ped->ichCaret = ped->cch;
  2005. } else {
  2006. // Move caret to end of line.
  2007. ped->ichCaret = ped->chLines[ped->iCaretLine] +
  2008. MLLine(ped, ped->iCaretLine);
  2009. }
  2010. // Update selection.
  2011. newMaxSel = ped->ichCaret;
  2012. if (scState & SHFTDOWN) {
  2013. if (MinEqCar && !MinEqMax) {
  2014. // Reduce selection
  2015. if (scState & CTRLDOWN) {
  2016. newMinSel = ped->ichMaxSel;
  2017. } else {
  2018. newMinSel = ped->ichCaret;
  2019. newMaxSel = ped->ichMaxSel;
  2020. }
  2021. }
  2022. } else {
  2023. // Clear selection
  2024. newMinSel = ped->ichCaret;
  2025. }
  2026. changeSelection = TRUE;
  2027. break;
  2028. // FE_IME // EC_INSERT_COMPOSITION_CHAR : MLKeyDown() : VK_HANJA support
  2029. case VK_HANJA:
  2030. if ( HanjaKeyHandler( ped ) ) {
  2031. changeSelection = TRUE;
  2032. newMinSel = ped->ichCaret;
  2033. newMaxSel = ped->ichCaret + (ped->fAnsi ? 2 : 1);
  2034. }
  2035. break;
  2036. case VK_PRIOR:
  2037. case VK_NEXT:
  2038. if (!(scState & CTRLDOWN)) {
  2039. /*
  2040. * Vertical scroll by one visual screen
  2041. */
  2042. hdc = ECGetEditDC(ped, TRUE);
  2043. MLIchToXYPos(ped, hdc, ped->ichCaret, FALSE, &mousePt);
  2044. ECReleaseEditDC(ped, hdc, TRUE);
  2045. mousePt.y += 1;
  2046. SendMessage(ped->hwnd, WM_VSCROLL, virtKeyCode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0L);
  2047. /*
  2048. * Move the cursor there
  2049. */
  2050. MLMouseMotion(ped, WM_LBUTTONDOWN, !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
  2051. MLMouseMotion(ped, WM_LBUTTONUP, !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
  2052. } else {
  2053. /*
  2054. * Horizontal scroll by one screenful minus one char
  2055. */
  2056. iScrollAmt = ((ped->rcFmt.right - ped->rcFmt.left) / ped->aveCharWidth) - 1;
  2057. if (virtKeyCode == VK_PRIOR)
  2058. iScrollAmt *= -1; /* For previous page */
  2059. SendMessage(ped->hwnd, WM_HSCROLL, MAKELONG(EM_LINESCROLL, iScrollAmt), 0);
  2060. break;
  2061. }
  2062. break;
  2063. case VK_DELETE:
  2064. if (ped->fReadOnly)
  2065. break;
  2066. switch (scState) {
  2067. case NONEDOWN:
  2068. /*
  2069. * Clear selection. If no selection, delete (clear) character
  2070. * right
  2071. */
  2072. if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) {
  2073. /*
  2074. * Move cursor forwards and send a backspace message...
  2075. */
  2076. if (ped->pLpkEditCallout) {
  2077. ped->ichMinSel = ped->ichCaret;
  2078. ped->ichMaxSel = MLMoveSelectionRestricted(ped, ped->ichCaret, FALSE);
  2079. } else {
  2080. ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, FALSE);
  2081. ped->ichMaxSel = ped->ichMinSel = ped->ichCaret;
  2082. }
  2083. goto DeleteAnotherChar;
  2084. }
  2085. break;
  2086. case SHFTDOWN:
  2087. /*
  2088. * CUT selection ie. remove and copy to clipboard, or if no
  2089. * selection, delete (clear) character left.
  2090. */
  2091. if (ped->ichMinSel == ped->ichMaxSel) {
  2092. goto DeleteAnotherChar;
  2093. } else {
  2094. SendMessage(ped->hwnd, WM_CUT, (UINT)0, 0L);
  2095. }
  2096. break;
  2097. case CTRLDOWN:
  2098. /*
  2099. * Clear selection, or delete to end of line if no selection
  2100. */
  2101. if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) {
  2102. ped->ichMaxSel = ped->ichCaret = ped->chLines[ped->iCaretLine] +
  2103. MLLine(ped, ped->iCaretLine);
  2104. }
  2105. break;
  2106. }
  2107. if (!(scState & SHFTDOWN) && (ped->ichMinSel != ped->ichMaxSel)) {
  2108. DeleteAnotherChar:
  2109. if (GETAPPVER() >= VER40) {
  2110. MLChar(ped, VK_BACK, 0);
  2111. } else {
  2112. SendMessageWorker(ped->pwnd, WM_CHAR, VK_BACK, 0, ped->fAnsi);
  2113. }
  2114. }
  2115. /*
  2116. * No need to update text or selection since BACKSPACE message does it
  2117. * for us.
  2118. */
  2119. break;
  2120. case VK_INSERT:
  2121. if (scState == CTRLDOWN || scState == SHFTDOWN) {
  2122. /*
  2123. * if CTRLDOWN Copy current selection to clipboard
  2124. */
  2125. /*
  2126. * if SHFTDOWN Paste clipboard
  2127. */
  2128. SendMessage(ped->hwnd, (UINT)(scState == CTRLDOWN ? WM_COPY : WM_PASTE), 0, 0);
  2129. }
  2130. break;
  2131. }
  2132. if (changeSelection) {
  2133. hdc = ECGetEditDC(ped, FALSE);
  2134. MLChangeSelection(ped, hdc, newMinSel, newMaxSel);
  2135. /*
  2136. * Set the caret's line
  2137. */
  2138. ped->iCaretLine = MLIchToLine(ped, ped->ichCaret);
  2139. if (virtKeyCode == VK_END &&
  2140. // Next line: Win95 Bug#11822, EditControl repaint (Sankar)
  2141. (ped->ichCaret == ped->chLines[ped->iCaretLine]) &&
  2142. ped->ichCaret < ped->cch &&
  2143. ped->fWrap && ped->iCaretLine > 0) {
  2144. LPSTR pText = ECLock(ped);
  2145. /*
  2146. * Handle moving to the end of a word wrapped line. This keeps the
  2147. * cursor from falling to the start of the next line if we have word
  2148. * wrapped and there is no CRLF.
  2149. */
  2150. if ( ped->fAnsi ) {
  2151. if (*(WORD UNALIGNED *)(pText +
  2152. ped->chLines[ped->iCaretLine] - 2) != 0x0A0D) {
  2153. ped->iCaretLine--;
  2154. }
  2155. } else {
  2156. if (*(DWORD UNALIGNED *)(pText +
  2157. (ped->chLines[ped->iCaretLine] - 2)*ped->cbChar) != 0x000A000D) {
  2158. ped->iCaretLine--;
  2159. }
  2160. }
  2161. CHECK_LINE_NUMBER(ped->iCaretLine, ped);
  2162. ECUnlock(ped);
  2163. }
  2164. /*
  2165. * Since drawtext sets the caret position
  2166. */
  2167. MLSetCaretPosition(ped, hdc);
  2168. ECReleaseEditDC(ped, hdc, FALSE);
  2169. /*
  2170. * Make sure we can see the cursor
  2171. */
  2172. MLEnsureCaretVisible(ped);
  2173. }
  2174. }
  2175. /***************************************************************************\
  2176. * MLChar
  2177. *
  2178. * Handles character and virtual key input
  2179. *
  2180. * History:
  2181. \***************************************************************************/
  2182. void MLChar(
  2183. PED ped,
  2184. DWORD keyValue,
  2185. int keyMods)
  2186. {
  2187. WCHAR keyPress;
  2188. BOOL updateText = FALSE;
  2189. /*
  2190. * keyValue is either:
  2191. * a Virtual Key (eg: VK_TAB, VK_ESCAPE, VK_BACK)
  2192. * a character (Unicode or "ANSI")
  2193. */
  2194. if (ped->fAnsi)
  2195. keyPress = LOBYTE(keyValue);
  2196. else
  2197. keyPress = LOWORD(keyValue);
  2198. if (ped->fMouseDown || keyPress == VK_ESCAPE) {
  2199. /*
  2200. * If we are in the middle of a mousedown command, don't do anything.
  2201. * Also, just ignore it if we get a translated escape key which happens
  2202. * with multiline edit controls in a dialog box.
  2203. */
  2204. return ;
  2205. }
  2206. ECInOutReconversionMode(ped, FALSE);
  2207. {
  2208. int scState;
  2209. scState = ECGetModKeys(keyMods);
  2210. if (ped->fInDialogBox && scState != CTRLDOWN) {
  2211. /*
  2212. * If this multiline edit control is in a dialog box, then we want the
  2213. * TAB key to take you to the next control, shift TAB to take you to the
  2214. * previous control, and CTRL-TAB to insert a tab into the edit control.
  2215. * We moved the focus when we received the keydown message so we will
  2216. * ignore the TAB key now unless the ctrl key is down. Also, we want
  2217. * CTRL-RETURN to insert a return into the text and RETURN to be sent to
  2218. * the default button.
  2219. */
  2220. if (keyPress == VK_TAB ||
  2221. (keyPress == VK_RETURN && !TestWF(ped->pwnd, EFWANTRETURN)))
  2222. return ;
  2223. }
  2224. /*
  2225. * Allow CTRL+C to copy from a read only edit control
  2226. * Ignore all other keys in read only controls
  2227. */
  2228. if ((ped->fReadOnly) && !((keyPress == 3) && (scState == CTRLDOWN))) {
  2229. return ;
  2230. }
  2231. }
  2232. switch (keyPress) {
  2233. case 0x0A: // linefeed
  2234. keyPress = VK_RETURN;
  2235. /*
  2236. * FALL THRU
  2237. */
  2238. case VK_RETURN:
  2239. case VK_TAB:
  2240. case VK_BACK:
  2241. DeleteSelection:
  2242. if (MLDeleteText(ped))
  2243. updateText = TRUE;
  2244. break;
  2245. default:
  2246. if (keyPress >= TEXT(' ')) {
  2247. /*
  2248. * If this is in [a-z],[A-Z] and we are an ES_NUMBER
  2249. * edit field, bail.
  2250. */
  2251. if (ped->f40Compat && TestWF(ped->pwnd, EFNUMBER)) {
  2252. if (!ECIsCharNumeric(ped, keyPress)) {
  2253. goto IllegalChar;
  2254. }
  2255. }
  2256. goto DeleteSelection;
  2257. }
  2258. break;
  2259. }
  2260. /*
  2261. * Handle key codes
  2262. */
  2263. switch(keyPress) {
  2264. UINT msg;
  2265. // Ctrl+Z == Undo
  2266. case 26:
  2267. msg = WM_UNDO;
  2268. goto SendEditingMessage;
  2269. break;
  2270. // Ctrl+X == Cut
  2271. case 24:
  2272. if (ped->ichMinSel == ped->ichMaxSel)
  2273. goto IllegalChar;
  2274. else
  2275. {
  2276. msg = WM_CUT;
  2277. goto SendEditingMessage;
  2278. }
  2279. break;
  2280. // Ctrl+C == Copy
  2281. case 3:
  2282. msg = WM_COPY;
  2283. goto SendEditingMessage;
  2284. break;
  2285. // Ctrl+V == Paste
  2286. case 22:
  2287. msg = WM_PASTE;
  2288. SendEditingMessage:
  2289. SendMessage(ped->hwnd, msg, 0, 0L);
  2290. break;
  2291. case VK_BACK:
  2292. //
  2293. // Delete any selected text or delete character left if no sel
  2294. //
  2295. if (!updateText && ped->ichMinSel)
  2296. {
  2297. //
  2298. // There was no selection to delete so we just delete
  2299. // character left if available
  2300. //
  2301. ped->ichMinSel = MLMoveSelection(ped, ped->ichCaret, TRUE);
  2302. MLDeleteText(ped);
  2303. }
  2304. break;
  2305. default:
  2306. if (keyPress == VK_RETURN)
  2307. if (ped->fAnsi)
  2308. keyValue = 0x0A0D;
  2309. else
  2310. keyValue = 0x000A000D;
  2311. if ( keyPress >= TEXT(' ')
  2312. || keyPress == VK_RETURN
  2313. || keyPress == VK_TAB
  2314. || keyPress == 0x1E // RS - Unicode block separator
  2315. || keyPress == 0x1F // US - Unicode segment separator
  2316. ) {
  2317. NtUserCallNoParam(SFI_ZZZHIDECURSORNOCAPTURE);
  2318. if (ped->fAnsi) {
  2319. //
  2320. // check if it's a leading byte of double byte character
  2321. //
  2322. if (ECIsDBCSLeadByte(ped,(BYTE)keyPress)) {
  2323. int DBCSkey;
  2324. if ((DBCSkey = DbcsCombine(ped->hwnd, keyPress)) != 0)
  2325. keyValue = DBCSkey;
  2326. }
  2327. MLInsertText(ped, (LPSTR)&keyValue, HIBYTE(keyValue) ? 2 : 1, TRUE);
  2328. } else
  2329. MLInsertText(ped, (LPSTR)&keyValue, HIWORD(keyValue) ? 2 : 1, TRUE);
  2330. } else {
  2331. IllegalChar:
  2332. NtUserMessageBeep(0);
  2333. }
  2334. break;
  2335. }
  2336. }
  2337. /***************************************************************************\
  2338. * MLPasteText AorW
  2339. *
  2340. * Pastes a line of text from the clipboard into the edit control
  2341. * starting at ped->ichCaret. Updates ichMaxSel and ichMinSel to point to the
  2342. * end of the inserted text. Notifies the parent if space cannot be
  2343. * allocated. Returns how many characters were inserted.
  2344. *
  2345. * History:
  2346. \***************************************************************************/
  2347. ICH PASCAL NEAR MLPasteText(
  2348. PED ped)
  2349. {
  2350. HANDLE hData;
  2351. LPSTR lpchClip;
  2352. ICH cchAdded = 0;
  2353. HCURSOR hCursorOld;
  2354. #ifdef UNDO_CLEANUP // #ifdef Added in Chicago - johnl
  2355. if (!ped->fAutoVScroll) {
  2356. /*
  2357. * Empty the undo buffer if this edit control limits the amount of text
  2358. * the user can add to the window rect. This is so that we can undo this
  2359. * operation if doing in causes us to exceed the window boundaries.
  2360. */
  2361. ECEmptyUndo(ped);
  2362. }
  2363. #endif
  2364. hCursorOld = NtUserSetCursor(LoadCursor(NULL, IDC_WAIT));
  2365. if (!OpenClipboard(ped->hwnd))
  2366. goto PasteExitNoCloseClip;
  2367. if (!(hData = GetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT)) ||
  2368. (GlobalFlags(hData) == GMEM_INVALID_HANDLE)) {
  2369. RIPMSG1(RIP_WARNING, "MLPasteText(): couldn't get a valid handle(%x)", hData);
  2370. goto PasteExit;
  2371. }
  2372. /*
  2373. * See if any text should be deleted
  2374. */
  2375. MLDeleteText(ped);
  2376. USERGLOBALLOCK(hData, lpchClip);
  2377. if (lpchClip == NULL) {
  2378. RIPMSG1(RIP_WARNING, "MLPasteText: USERGLOBALLOCK(%x) failed.", hData);
  2379. goto PasteExit;
  2380. }
  2381. /*
  2382. * Get the length of the addition.
  2383. */
  2384. if (ped->fAnsi)
  2385. cchAdded = strlen(lpchClip);
  2386. else
  2387. cchAdded = wcslen((LPWSTR)lpchClip);
  2388. /*
  2389. * Insert the text (MLInsertText checks line length)
  2390. */
  2391. cchAdded = MLInsertText(ped, lpchClip, cchAdded, FALSE);
  2392. USERGLOBALUNLOCK(hData);
  2393. PasteExit:
  2394. NtUserCloseClipboard();
  2395. PasteExitNoCloseClip:
  2396. NtUserSetCursor(hCursorOld);
  2397. return (cchAdded);
  2398. }
  2399. /***************************************************************************\
  2400. * MLMouseMotion AorW
  2401. *
  2402. * History:
  2403. \***************************************************************************/
  2404. void MLMouseMotion(
  2405. PED ped,
  2406. UINT message,
  2407. UINT virtKeyDown,
  2408. LPPOINT mousePt)
  2409. {
  2410. BOOL fChangedSel = FALSE;
  2411. HDC hdc = ECGetEditDC(ped, TRUE);
  2412. ICH ichMaxSel = ped->ichMaxSel;
  2413. ICH ichMinSel = ped->ichMinSel;
  2414. ICH mouseCch;
  2415. ICH mouseLine;
  2416. int i, j;
  2417. LONG ll, lh;
  2418. mouseCch = MLMouseToIch(ped, hdc, mousePt, &mouseLine);
  2419. /*
  2420. * Save for timer
  2421. */
  2422. ped->ptPrevMouse = *mousePt;
  2423. ped->prevKeys = virtKeyDown;
  2424. switch (message) {
  2425. case WM_LBUTTONDBLCLK:
  2426. /*
  2427. * if shift key is down, extend selection to word we double clicked on
  2428. * else clear current selection and select word.
  2429. */
  2430. // LiZ -- 5/5/93
  2431. if (ped->fAnsi && ped->fDBCS) {
  2432. LPSTR pText = ECLock(ped);
  2433. ECWord(ped,ped->ichCaret,
  2434. ECIsDBCSLeadByte(ped, *(pText+(ped->ichCaret)))
  2435. ? FALSE :
  2436. (ped->ichCaret == ped->chLines[ped->iCaretLine]
  2437. ? FALSE : TRUE), &ll, &lh);
  2438. ECUnlock(ped);
  2439. } else {
  2440. ECWord(ped, mouseCch, !(mouseCch == ped->chLines[mouseLine]), &ll, &lh);
  2441. }
  2442. if (!(virtKeyDown & MK_SHIFT)) {
  2443. // If shift key isn't down, move caret to mouse point and clear
  2444. // old selection
  2445. ichMinSel = ll;
  2446. ichMaxSel = ped->ichCaret = lh;
  2447. } else {
  2448. // Shiftkey is down so we want to maintain the current selection
  2449. // (if any) and just extend or reduce it
  2450. if (ped->ichMinSel == ped->ichCaret) {
  2451. ichMinSel = ped->ichCaret = ll;
  2452. ECWord(ped, ichMaxSel, TRUE, &ll, &lh);
  2453. } else {
  2454. ichMaxSel = ped->ichCaret = lh;
  2455. ECWord(ped, ichMinSel, FALSE, &ll, &lh);
  2456. }
  2457. }
  2458. ped->ichStartMinSel = ll;
  2459. ped->ichStartMaxSel = lh;
  2460. goto InitDragSelect;
  2461. case WM_MOUSEMOVE:
  2462. if (ped->fMouseDown) {
  2463. /*
  2464. * Set the system timer to automatically scroll when mouse is
  2465. * outside of the client rectangle. Speed of scroll depends on
  2466. * distance from window.
  2467. */
  2468. i = mousePt->y < 0 ? -mousePt->y : mousePt->y - ped->rcFmt.bottom;
  2469. j = gpsi->dtScroll - ((UINT)i << 4);
  2470. if (j < 1)
  2471. j = 1;
  2472. NtUserSetSystemTimer(ped->hwnd, IDSYS_SCROLL, (UINT)j, NULL);
  2473. fChangedSel = TRUE;
  2474. // Extend selection, move caret right
  2475. if (ped->ichStartMinSel || ped->ichStartMaxSel) {
  2476. // We're in WORD SELECT mode
  2477. BOOL fReverse = (mouseCch <= ped->ichStartMinSel);
  2478. ECWord(ped, mouseCch, !fReverse, &ll, &lh);
  2479. if (fReverse) {
  2480. ichMinSel = ped->ichCaret = ll;
  2481. ichMaxSel = ped->ichStartMaxSel;
  2482. } else {
  2483. ichMinSel = ped->ichStartMinSel;
  2484. ichMaxSel = ped->ichCaret = lh;
  2485. }
  2486. } else if ((ped->ichMinSel == ped->ichCaret) &&
  2487. (ped->ichMinSel != ped->ichMaxSel))
  2488. // Reduce selection extent
  2489. ichMinSel = ped->ichCaret = mouseCch;
  2490. else
  2491. // Extend selection extent
  2492. ichMaxSel = ped->ichCaret = mouseCch;
  2493. ped->iCaretLine = mouseLine;
  2494. }
  2495. break;
  2496. case WM_LBUTTONDOWN:
  2497. ll = lh = mouseCch;
  2498. if (!(virtKeyDown & MK_SHIFT)) {
  2499. // If shift key isn't down, move caret to mouse point and clear
  2500. // old selection
  2501. ichMinSel = ichMaxSel = ped->ichCaret = mouseCch;
  2502. } else {
  2503. // Shiftkey is down so we want to maintain the current selection
  2504. // (if any) and just extend or reduce it
  2505. if (ped->ichMinSel == ped->ichCaret)
  2506. ichMinSel = ped->ichCaret = mouseCch;
  2507. else
  2508. ichMaxSel = ped->ichCaret = mouseCch;
  2509. }
  2510. ped->ichStartMinSel = ped->ichStartMaxSel = 0;
  2511. InitDragSelect:
  2512. ped->iCaretLine = mouseLine;
  2513. ped->fMouseDown = FALSE;
  2514. NtUserSetCapture(ped->hwnd);
  2515. ped->fMouseDown = TRUE;
  2516. fChangedSel = TRUE;
  2517. // Set the timer so that we can scroll automatically when the mouse
  2518. // is moved outside the window rectangle.
  2519. NtUserSetSystemTimer(ped->hwnd, IDSYS_SCROLL, gpsi->dtScroll, NULL);
  2520. break;
  2521. case WM_LBUTTONUP:
  2522. if (ped->fMouseDown) {
  2523. /*
  2524. * Kill the timer so that we don't do auto mouse moves anymore
  2525. */
  2526. NtUserKillSystemTimer(ped->hwnd, IDSYS_SCROLL);
  2527. NtUserReleaseCapture();
  2528. MLSetCaretPosition(ped, hdc);
  2529. ped->fMouseDown = FALSE;
  2530. }
  2531. break;
  2532. }
  2533. if (fChangedSel) {
  2534. MLChangeSelection(ped, hdc, ichMinSel, ichMaxSel);
  2535. MLEnsureCaretVisible(ped);
  2536. }
  2537. ECReleaseEditDC(ped, hdc, TRUE);
  2538. if (!ped->fFocus && (message == WM_LBUTTONDOWN)) {
  2539. /*
  2540. * If we don't have the focus yet, get it
  2541. */
  2542. NtUserSetFocus(ped->hwnd);
  2543. }
  2544. }
  2545. /***************************************************************************\
  2546. * MLScroll AorW
  2547. *
  2548. * History:
  2549. \***************************************************************************/
  2550. LONG MLScroll(
  2551. PED ped,
  2552. BOOL fVertical,
  2553. int cmd,
  2554. int iAmt,
  2555. BOOL fRedraw)
  2556. {
  2557. SCROLLINFO si;
  2558. int dx = 0;
  2559. int dy = 0;
  2560. BOOL fIncludeLeftMargin;
  2561. int newPos;
  2562. int oldPos;
  2563. BOOL fUp = FALSE;
  2564. UINT wFlag;
  2565. DWORD dwTime = 0;
  2566. if (fRedraw && (cmd != ML_REFRESH)) {
  2567. UpdateWindow(ped->hwnd);
  2568. }
  2569. if (ped->pLpkEditCallout && ped->fRtoLReading && !fVertical
  2570. && ped->maxPixelWidth > ped->rcFmt.right - ped->rcFmt.left) {
  2571. /*
  2572. * Horizontal scoll of a right oriented window with a scrollbar.
  2573. * Map the logical xOffset to visual coordinates.
  2574. */
  2575. oldPos = ped->maxPixelWidth
  2576. - ((int)ped->xOffset + ped->rcFmt.right - ped->rcFmt.left);
  2577. } else
  2578. oldPos = (int) (fVertical ? ped->ichScreenStart : ped->xOffset);
  2579. fIncludeLeftMargin = (ped->xOffset == 0);
  2580. switch (cmd) {
  2581. case ML_REFRESH:
  2582. newPos = oldPos;
  2583. break;
  2584. case EM_GETTHUMB:
  2585. return(oldPos);
  2586. case SB_THUMBTRACK:
  2587. case SB_THUMBPOSITION:
  2588. /*
  2589. * If the edit contains more than 0xFFFF lines
  2590. * it means that the scrolbar can return a position
  2591. * that cannot fit in a WORD (16 bits), so use
  2592. * GetScrollInfo (which is slower) in this case.
  2593. */
  2594. if (ped->cLines < 0xFFFF) {
  2595. newPos = iAmt;
  2596. } else {
  2597. SCROLLINFO si;
  2598. si.cbSize = sizeof(SCROLLINFO);
  2599. si.fMask = SIF_TRACKPOS;
  2600. GetScrollInfo( ped->hwnd, SB_VERT, &si);
  2601. newPos = si.nTrackPos;
  2602. }
  2603. break;
  2604. case SB_TOP: // == SB_LEFT
  2605. newPos = 0;
  2606. break;
  2607. case SB_BOTTOM: // == SB_RIGHT
  2608. if (fVertical)
  2609. newPos = ped->cLines;
  2610. else
  2611. newPos = ped->maxPixelWidth;
  2612. break;
  2613. case SB_PAGEUP: // == SB_PAGELEFT
  2614. fUp = TRUE;
  2615. case SB_PAGEDOWN: // == SB_PAGERIGHT
  2616. if (fVertical)
  2617. iAmt = ped->ichLinesOnScreen - 1;
  2618. else
  2619. iAmt = (ped->rcFmt.right - ped->rcFmt.left) - 1;
  2620. if (iAmt == 0)
  2621. iAmt++;
  2622. if (fUp)
  2623. iAmt = -iAmt;
  2624. goto AddDelta;
  2625. case SB_LINEUP: // == SB_LINELEFT
  2626. fUp = TRUE;
  2627. case SB_LINEDOWN: // == SB_LINERIGHT
  2628. dwTime = iAmt;
  2629. iAmt = 1;
  2630. if (fUp)
  2631. iAmt = -iAmt;
  2632. // | |
  2633. // | FALL THRU |
  2634. // V V
  2635. case EM_LINESCROLL:
  2636. if (!fVertical)
  2637. iAmt *= ped->aveCharWidth;
  2638. AddDelta:
  2639. newPos = oldPos + iAmt;
  2640. break;
  2641. default:
  2642. return(0L);
  2643. }
  2644. if (fVertical) {
  2645. if (si.nMax = ped->cLines)
  2646. si.nMax--;
  2647. if (!ped->hwndParent ||
  2648. TestWF(ValidateHwnd(ped->hwndParent), WFWIN40COMPAT))
  2649. si.nPage = ped->ichLinesOnScreen;
  2650. else
  2651. si.nPage = 0;
  2652. wFlag = WFVSCROLL;
  2653. } else {
  2654. si.nMax = ped->maxPixelWidth;
  2655. si.nPage = ped->rcFmt.right - ped->rcFmt.left;
  2656. wFlag = WFHSCROLL;
  2657. }
  2658. if (TestWF(ValidateHwnd(ped->hwnd), wFlag)) {
  2659. si.cbSize = sizeof(SCROLLINFO);
  2660. si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
  2661. si.nMin = 0;
  2662. si.nPos = newPos;
  2663. newPos = SetScrollInfo(ped->hwnd, fVertical ? SB_VERT : SB_HORZ,
  2664. &si, fRedraw);
  2665. } else {
  2666. // BOGUS -- this is duped code from ScrollBar code
  2667. // but it's for the case when we want to limit the position without
  2668. // actually having the scroll bar
  2669. int iMaxPos;
  2670. // Clip page to 0, range + 1
  2671. si.nPage = max(min((int)si.nPage, si.nMax + 1), 0);
  2672. iMaxPos = si.nMax - (si.nPage ? si.nPage - 1 : 0);
  2673. newPos = min(max(newPos, 0), iMaxPos);
  2674. }
  2675. oldPos -= newPos;
  2676. if (!oldPos)
  2677. return(0L);
  2678. if (ped->pLpkEditCallout && ped->fRtoLReading && !fVertical
  2679. && ped->maxPixelWidth > ped->rcFmt.right - ped->rcFmt.left) {
  2680. // Map visual oldPos and newPos back to logical coordinates
  2681. newPos = ped->maxPixelWidth
  2682. - (newPos + ped->rcFmt.right - ped->rcFmt.left);
  2683. oldPos = -oldPos;
  2684. if (newPos<0) {
  2685. // Compensate for scroll bar returning pos > max-page
  2686. oldPos += newPos;
  2687. newPos=0;
  2688. }
  2689. }
  2690. if (fVertical) {
  2691. ped->ichScreenStart = newPos;
  2692. dy = oldPos * ped->lineHeight;
  2693. } else {
  2694. ped->xOffset = newPos;
  2695. dx = oldPos;
  2696. }
  2697. if (cmd != SB_THUMBTRACK)
  2698. // We don't want to notify the parent of thumbtracking since they might
  2699. // try to set the thumb position to something bogus.
  2700. // NOTEPAD used to be guilty of this -- but I rewrote it so it's not.
  2701. // The question is WHO ELSE does this? (jeffbog)
  2702. ECNotifyParent(ped, fVertical ? EN_VSCROLL : EN_HSCROLL);
  2703. if (fRedraw && _IsWindowVisible(ped->pwnd)) {
  2704. RECT rc;
  2705. RECT rcUpdate;
  2706. RECT rcClipRect;
  2707. HDC hdc;
  2708. _GetClientRect(ped->pwnd, &rc);
  2709. CopyRect(&rcClipRect, &ped->rcFmt);
  2710. if (fVertical) { // Is this a vertical scroll?
  2711. rcClipRect.left -= ped->wLeftMargin;
  2712. rcClipRect.right += ped->wRightMargin;
  2713. }
  2714. IntersectRect(&rc, &rc, &rcClipRect);
  2715. rc.bottom++;
  2716. /*
  2717. * Chicago has this HideCaret but there doesn't appear to be a
  2718. * corresponding ShowCaret, so we lose the Caret under NT when the
  2719. * EC scrolls - Johnl
  2720. *
  2721. * HideCaret(ped->hwnd);
  2722. */
  2723. hdc = ECGetEditDC(ped, FALSE);
  2724. ECSetEditClip(ped, hdc, fIncludeLeftMargin);
  2725. if (ped->hFont)
  2726. SelectObject(hdc, ped->hFont);
  2727. ECGetBrush(ped, hdc);
  2728. if (ped->pLpkEditCallout && !fVertical) {
  2729. // Horizontal scroll with complex script support
  2730. int xFarOffset = ped->xOffset + ped->rcFmt.right - ped->rcFmt.left;
  2731. rc = ped->rcFmt;
  2732. if (dwTime != 0)
  2733. ScrollWindowEx(ped->hwnd, ped->fRtoLReading ? -dx : dx, dy, NULL, NULL, NULL,
  2734. &rcUpdate, MAKELONG(SW_SMOOTHSCROLL | SW_SCROLLCHILDREN, dwTime));
  2735. else
  2736. NtUserScrollDC(hdc, ped->fRtoLReading ? -dx : dx, dy,
  2737. &rc, &rc, NULL, &rcUpdate);
  2738. // Handle margins: Blank if clipped by horizontal scrolling,
  2739. // display otherwise.
  2740. if (ped->wLeftMargin) {
  2741. rc.left = ped->rcFmt.left - ped->wLeftMargin;
  2742. rc.right = ped->rcFmt.left;
  2743. if ( (ped->format != ES_LEFT) // Always display margin for centred or far-aligned text
  2744. || // Display LTR left margin if first character fully visible
  2745. (!ped->fRtoLReading && ped->xOffset == 0)
  2746. || // Display RTL left margin if last character fully visible
  2747. (ped->fRtoLReading && xFarOffset >= ped->maxPixelWidth)) {
  2748. UnionRect(&rcUpdate, &rcUpdate, &rc);
  2749. } else {
  2750. ExtTextOutW(hdc, rc.left, rc.top,
  2751. ETO_CLIPPED | ETO_OPAQUE | ETO_GLYPH_INDEX,
  2752. &rc, L"", 0, 0L);
  2753. }
  2754. }
  2755. if (ped->wRightMargin) {
  2756. rc.left = ped->rcFmt.right;
  2757. rc.right = ped->rcFmt.right + ped->wRightMargin;
  2758. if ( (ped->format != ES_LEFT) // Always display margin for centred or far-aligned text
  2759. || // Display RTL right margin if first character fully visible
  2760. (ped->fRtoLReading && ped->xOffset == 0)
  2761. || // Display LTR right margin if last character fully visible
  2762. (!ped->fRtoLReading && xFarOffset >= ped->maxPixelWidth)) {
  2763. UnionRect(&rcUpdate, &rcUpdate, &rc);
  2764. } else {
  2765. ExtTextOutW(hdc, rc.left, rc.top,
  2766. ETO_CLIPPED | ETO_OPAQUE | ETO_GLYPH_INDEX,
  2767. &rc, L"", 0, 0L);
  2768. }
  2769. }
  2770. } else {
  2771. if (dwTime != 0)
  2772. ScrollWindowEx(ped->hwnd, dx, dy, NULL, NULL, NULL,
  2773. &rcUpdate, MAKELONG(SW_SMOOTHSCROLL | SW_SCROLLCHILDREN, dwTime));
  2774. else
  2775. NtUserScrollDC(hdc, dx, dy, &rc, &rc, NULL, &rcUpdate);
  2776. // If we need to wipe out the left margin area
  2777. if (ped->wLeftMargin && !fVertical) {
  2778. // Calculate the rectangle to be wiped out
  2779. rc.right = rc.left;
  2780. rc.left = max(0, (INT)(ped->rcFmt.left - ped->wLeftMargin));
  2781. if (rc.left < rc.right) {
  2782. if (fIncludeLeftMargin && (ped->xOffset != 0)) {
  2783. ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED | ETO_OPAQUE,
  2784. &rc, L"", 0, 0L);
  2785. } else
  2786. if((!fIncludeLeftMargin) && (ped->xOffset == 0))
  2787. UnionRect(&rcUpdate, &rcUpdate, &rc);
  2788. }
  2789. }
  2790. }
  2791. MLSetCaretPosition(ped,hdc);
  2792. ECReleaseEditDC(ped, hdc, FALSE);
  2793. NtUserInvalidateRect(ped->hwnd, &rcUpdate,
  2794. ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines));
  2795. UpdateWindow(ped->hwnd);
  2796. }
  2797. return(MAKELONG(-oldPos, 1));
  2798. }
  2799. /***************************************************************************\
  2800. * MLSetFocus AorW
  2801. *
  2802. * Gives the edit control the focus and notifies the parent
  2803. * EN_SETFOCUS.
  2804. *
  2805. * History:
  2806. \***************************************************************************/
  2807. void MLSetFocus(
  2808. PED ped)
  2809. {
  2810. HDC hdc;
  2811. if (!ped->fFocus) {
  2812. ped->fFocus = 1; /* Set focus */
  2813. hdc = ECGetEditDC(ped, TRUE);
  2814. /*
  2815. * Draw the caret. We need to do this even if the window is hidden
  2816. * because in dlg box initialization time we may set the focus to a
  2817. * hidden edit control window. If we don't create the caret etc, it will
  2818. * never end up showing properly.
  2819. */
  2820. if (ped->pLpkEditCallout) {
  2821. ped->pLpkEditCallout->EditCreateCaret (ped, hdc, ECGetCaretWidth(), ped->lineHeight, 0);
  2822. }
  2823. else {
  2824. NtUserCreateCaret(ped->hwnd, (HBITMAP)NULL, ECGetCaretWidth(), ped->lineHeight);
  2825. }
  2826. NtUserShowCaret(ped->hwnd);
  2827. MLSetCaretPosition(ped, hdc);
  2828. /*
  2829. * Show the current selection. Only if the selection was hidden when we
  2830. * lost the focus, must we invert (show) it.
  2831. */
  2832. if (!ped->fNoHideSel && ped->ichMinSel != ped->ichMaxSel &&
  2833. _IsWindowVisible(ped->pwnd))
  2834. MLDrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel, TRUE);
  2835. ECReleaseEditDC(ped, hdc, TRUE);
  2836. }
  2837. #if 0
  2838. MLEnsureCaretVisible(ped);
  2839. #endif
  2840. /*
  2841. * Notify parent we have the focus
  2842. */
  2843. ECNotifyParent(ped, EN_SETFOCUS);
  2844. }
  2845. /***************************************************************************\
  2846. * MLKillFocus AorW
  2847. *
  2848. * The edit control loses the focus and notifies the parent via
  2849. * EN_KILLFOCUS.
  2850. *
  2851. * History:
  2852. \***************************************************************************/
  2853. void MLKillFocus(
  2854. PED ped)
  2855. {
  2856. HDC hdc;
  2857. /*
  2858. * Reset the wheel delta count.
  2859. */
  2860. gcWheelDelta = 0;
  2861. if (ped->fFocus) {
  2862. ped->fFocus = 0; /* Clear focus */
  2863. /*
  2864. * Do this only if we still have the focus. But we always notify the
  2865. * parent that we lost the focus whether or not we originally had the
  2866. * focus.
  2867. */
  2868. /*
  2869. * Hide the current selection if needed
  2870. */
  2871. if (!ped->fNoHideSel && ped->ichMinSel != ped->ichMaxSel &&
  2872. _IsWindowVisible(ped->pwnd)) {
  2873. hdc = ECGetEditDC(ped, FALSE);
  2874. MLDrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel, TRUE);
  2875. ECReleaseEditDC(ped, hdc, FALSE);
  2876. }
  2877. /*
  2878. * Destroy the caret
  2879. */
  2880. NtUserDestroyCaret();
  2881. }
  2882. /*
  2883. * Notify parent that we lost the focus.
  2884. */
  2885. ECNotifyParent(ped, EN_KILLFOCUS);
  2886. }
  2887. /***************************************************************************\
  2888. * MLEnsureCaretVisible AorW
  2889. *
  2890. * Scrolls the caret into the visible region.
  2891. * Returns TRUE if scrolling was done else return s FALSE.
  2892. *
  2893. * History:
  2894. \***************************************************************************/
  2895. BOOL MLEnsureCaretVisible(
  2896. PED ped)
  2897. {
  2898. UINT iLineMax;
  2899. int xposition;
  2900. BOOL fPrevLine;
  2901. HDC hdc;
  2902. BOOL fVScroll = FALSE;
  2903. BOOL fHScroll = FALSE;
  2904. if (_IsWindowVisible(ped->pwnd)) {
  2905. int iAmt;
  2906. int iFmtWidth = ped->rcFmt.right - ped->rcFmt.left;
  2907. if (ped->fAutoVScroll) {
  2908. iLineMax = ped->ichScreenStart + ped->ichLinesOnScreen - 1;
  2909. if (fVScroll = (ped->iCaretLine > iLineMax))
  2910. iAmt = iLineMax;
  2911. else if (fVScroll = (ped->iCaretLine < ped->ichScreenStart))
  2912. iAmt = ped->ichScreenStart;
  2913. if (fVScroll)
  2914. MLScroll(ped, TRUE, EM_LINESCROLL, ped->iCaretLine - iAmt, TRUE);
  2915. }
  2916. if (ped->fAutoHScroll && ((int) ped->maxPixelWidth > iFmtWidth)) {
  2917. POINT pt;
  2918. /* Get the current position of the caret in pixels */
  2919. if ((UINT) (ped->cLines - 1) != ped->iCaretLine &&
  2920. ped->ichCaret == ped->chLines[ped->iCaretLine + 1])
  2921. fPrevLine = TRUE;
  2922. else
  2923. fPrevLine = FALSE;
  2924. hdc = ECGetEditDC(ped,TRUE);
  2925. MLIchToXYPos(ped, hdc, ped->ichCaret, fPrevLine, &pt);
  2926. ECReleaseEditDC(ped, hdc, TRUE);
  2927. xposition = pt.x;
  2928. // Remember, MLIchToXYPos returns coordinates with respect to the
  2929. // top left pixel displayed on the screen. Thus, if xPosition < 0,
  2930. // it means xPosition is less than current ped->xOffset.
  2931. iFmtWidth /= 3;
  2932. if (fHScroll = (xposition < ped->rcFmt.left))
  2933. // scroll to the left
  2934. iAmt = ped->rcFmt.left + iFmtWidth;
  2935. else if (fHScroll = (xposition > ped->rcFmt.right))
  2936. // scroll to the right
  2937. iAmt = ped->rcFmt.right - iFmtWidth;
  2938. if (fHScroll)
  2939. MLScroll(ped, FALSE, EM_LINESCROLL, (xposition - iAmt) / ped->aveCharWidth, TRUE);
  2940. }
  2941. }
  2942. return(fVScroll);
  2943. }
  2944. /***************************************************************************\
  2945. * MLEditWndProc
  2946. *
  2947. * Class procedure for all multi line edit controls.
  2948. * Dispatches all messages to the appropriate handlers which are named
  2949. * as follows:
  2950. * SL (single line) prefixes all single line edit control procedures while
  2951. * EC (edit control) prefixes all common handlers.
  2952. *
  2953. * The MLEditWndProc only handles messages specific to multi line edit
  2954. * controls.
  2955. *
  2956. * WARNING: If you add a message here, add it to gawEditWndProc[] in
  2957. * kernel\server.c too, otherwise EditWndProcA/W will send it straight to
  2958. * DefWindowProcWorker
  2959. *
  2960. * History:
  2961. \***************************************************************************/
  2962. LRESULT MLEditWndProc(
  2963. HWND hwnd,
  2964. PED ped,
  2965. UINT message,
  2966. WPARAM wParam,
  2967. LPARAM lParam)
  2968. {
  2969. HDC hdc;
  2970. PAINTSTRUCT ps;
  2971. LPRECT lprc;
  2972. POINT pt;
  2973. DWORD windowstyle;
  2974. switch (message) {
  2975. case WM_INPUTLANGCHANGE:
  2976. if (ped && ped->fFocus && ped->pLpkEditCallout) {
  2977. NtUserHideCaret(hwnd);
  2978. hdc = ECGetEditDC(ped, TRUE);
  2979. NtUserDestroyCaret();
  2980. ped->pLpkEditCallout->EditCreateCaret (ped, hdc, ECGetCaretWidth(), ped->lineHeight, (UINT)lParam);
  2981. MLSetCaretPosition(ped, hdc);
  2982. ECReleaseEditDC(ped, hdc, TRUE);
  2983. NtUserShowCaret(hwnd);
  2984. }
  2985. goto PassToDefaultWindowProc;
  2986. case WM_STYLECHANGED:
  2987. if (ped && ped->pLpkEditCallout) {
  2988. switch (wParam) {
  2989. case GWL_STYLE:
  2990. ECUpdateFormat(ped,
  2991. ((LPSTYLESTRUCT)lParam)->styleNew,
  2992. GetWindowLong(ped->hwnd, GWL_EXSTYLE));
  2993. return 1L;
  2994. case GWL_EXSTYLE:
  2995. ECUpdateFormat(ped,
  2996. GetWindowLong(ped->hwnd, GWL_STYLE),
  2997. ((LPSTYLESTRUCT)lParam)->styleNew);
  2998. return 1L;
  2999. }
  3000. }
  3001. goto PassToDefaultWindowProc;
  3002. case WM_CHAR:
  3003. /*
  3004. * wParam - the value of the key
  3005. * lParam - modifiers, repeat count etc (not used)
  3006. */
  3007. MLChar(ped, (UINT)wParam, 0);
  3008. break;
  3009. case WM_ERASEBKGND: {
  3010. HBRUSH hbr;
  3011. // USE SAME RULES AS IN ECGetBrush()
  3012. if (ped->f40Compat &&
  3013. (ped->fReadOnly || ped->fDisabled))
  3014. hbr = (HBRUSH) CTLCOLOR_STATIC;
  3015. else
  3016. hbr = (HBRUSH) CTLCOLOR_EDIT;
  3017. FillWindow(ped->hwndParent, hwnd, (HDC)wParam, hbr);
  3018. }
  3019. return ((LONG)TRUE);
  3020. case WM_GETDLGCODE: {
  3021. LONG code = DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS | DLGC_WANTALLKEYS;
  3022. /*
  3023. ** !!! JEFFBOG HACK !!!
  3024. ** Only set Dialog Box Flag if GETDLGCODE message is generated by
  3025. ** IsDialogMessage -- if so, the lParam will be a pointer to the
  3026. ** message structure passed to IsDialogMessage; otherwise, lParam
  3027. ** will be NULL. Reason for the HACK alert: the wParam & lParam
  3028. ** for GETDLGCODE is still not clearly defined and may end up
  3029. ** changing in a way that would throw this off
  3030. **
  3031. */
  3032. if (lParam)
  3033. ped->fInDialogBox = TRUE; // Mark ML edit ctrl as in a dialog box
  3034. /*
  3035. ** If this is a WM_SYSCHAR message generated by the UNDO keystroke
  3036. ** we want this message so we can EAT IT in "case WM_SYSCHAR:"
  3037. */
  3038. if (lParam && (((LPMSG)lParam)->message == WM_SYSCHAR) &&
  3039. ((DWORD)((LPMSG)lParam)->lParam & SYS_ALTERNATE) &&
  3040. ((WORD)wParam == VK_BACK))
  3041. code |= DLGC_WANTMESSAGE;
  3042. return code;
  3043. }
  3044. case EM_SCROLL:
  3045. message = WM_VSCROLL;
  3046. /*
  3047. * FALL THROUGH
  3048. */
  3049. case WM_HSCROLL:
  3050. case WM_VSCROLL:
  3051. return MLScroll(ped, (message==WM_VSCROLL), LOWORD(wParam), HIWORD(wParam), TRUE);
  3052. case WM_MOUSEWHEEL:
  3053. /*
  3054. * Don't handle zoom and datazoom.
  3055. */
  3056. if (wParam & (MK_SHIFT | MK_CONTROL)) {
  3057. goto PassToDefaultWindowProc;
  3058. }
  3059. gcWheelDelta -= (short) HIWORD(wParam);
  3060. windowstyle = ped->pwnd->style;
  3061. if ( abs(gcWheelDelta) >= WHEEL_DELTA &&
  3062. gpsi->ucWheelScrollLines > 0 &&
  3063. (windowstyle & (WS_VSCROLL | WS_HSCROLL))) {
  3064. int cLineScroll;
  3065. BOOL fVert;
  3066. int cPage;
  3067. if (windowstyle & WS_VSCROLL) {
  3068. fVert = TRUE;
  3069. cPage = ped->ichLinesOnScreen;
  3070. } else {
  3071. fVert = FALSE;
  3072. cPage = (ped->rcFmt.right - ped->rcFmt.left) / ped->aveCharWidth;
  3073. }
  3074. /*
  3075. * Limit a roll of one (1) WHEEL_DELTA to scroll one (1) page.
  3076. */
  3077. cLineScroll = (int) min(
  3078. (UINT) (max(1, (cPage - 1))),
  3079. gpsi->ucWheelScrollLines);
  3080. cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
  3081. UserAssert(cLineScroll != 0);
  3082. gcWheelDelta = gcWheelDelta % WHEEL_DELTA;
  3083. MLScroll(ped, fVert, EM_LINESCROLL, cLineScroll, TRUE);
  3084. }
  3085. break;
  3086. case WM_KEYDOWN:
  3087. /*
  3088. * wParam - virt keycode of the given key
  3089. * lParam - modifiers such as repeat count etc. (not used)
  3090. */
  3091. MLKeyDown(ped, (UINT)wParam, 0);
  3092. break;
  3093. case WM_KILLFOCUS:
  3094. /*
  3095. * wParam - handle of the window that receives the input focus
  3096. * lParam - not used
  3097. */
  3098. MLKillFocus(ped);
  3099. break;
  3100. case WM_CAPTURECHANGED:
  3101. //
  3102. // wParam -- unused
  3103. // lParam -- hwnd of window gaining capture.
  3104. //
  3105. if (ped->fMouseDown) {
  3106. //
  3107. // We don't change the caret pos here. If this is happening
  3108. // due to button up, then we'll change the pos in the
  3109. // handler after ReleaseCapture(). Otherwise, just end
  3110. // gracefully because someone else has stolen capture out
  3111. // from under us.
  3112. //
  3113. ped->fMouseDown = FALSE;
  3114. NtUserKillSystemTimer(ped->hwnd, IDSYS_SCROLL);
  3115. }
  3116. break;
  3117. case WM_SYSTIMER:
  3118. /*
  3119. * This allows us to automatically scroll if the user holds the mouse
  3120. * outside the edit control window. We simulate mouse moves at timer
  3121. * intervals set in MouseMotionHandler.
  3122. */
  3123. if (ped->fMouseDown)
  3124. MLMouseMotion(ped, WM_MOUSEMOVE, ped->prevKeys, &ped->ptPrevMouse);
  3125. break;
  3126. case WM_MBUTTONDOWN:
  3127. EnterReaderModeHelper(ped->hwnd);
  3128. break;
  3129. case WM_MOUSEMOVE:
  3130. UserAssert(ped->fMouseDown);
  3131. /*
  3132. * FALL THROUGH
  3133. */
  3134. case WM_LBUTTONDBLCLK:
  3135. case WM_LBUTTONDOWN:
  3136. case WM_LBUTTONUP:
  3137. /*
  3138. * wParam - contains a value that indicates which virtual keys are down
  3139. lParam - contains x and y coords of the mouse cursor
  3140. */
  3141. POINTSTOPOINT(pt, lParam);
  3142. MLMouseMotion(ped, message, (UINT)wParam, &pt);
  3143. break;
  3144. case WM_CREATE:
  3145. /*
  3146. * wParam - handle to window being created
  3147. * lParam - points to a CREATESTRUCT that contains copies of parameters
  3148. * passed to the CreateWindow function.
  3149. */
  3150. return (MLCreate(ped, (LPCREATESTRUCT)lParam));
  3151. case WM_PRINTCLIENT:
  3152. MLPaint(ped, (HDC) wParam, NULL);
  3153. break;
  3154. case WM_PAINT:
  3155. /*
  3156. * wParam - can be hdc from subclassed paint
  3157. lParam - not used
  3158. */
  3159. if (wParam) {
  3160. hdc = (HDC) wParam;
  3161. lprc = NULL;
  3162. } else {
  3163. hdc = NtUserBeginPaint(ped->hwnd, &ps);
  3164. lprc = &ps.rcPaint;
  3165. }
  3166. if (_IsWindowVisible(ped->pwnd))
  3167. MLPaint(ped, hdc, lprc);
  3168. if (!wParam)
  3169. NtUserEndPaint(ped->hwnd, &ps);
  3170. break;
  3171. case WM_PASTE:
  3172. /*
  3173. * wParam - not used
  3174. lParam - not used
  3175. */
  3176. if (!ped->fReadOnly)
  3177. MLPasteText(ped);
  3178. break;
  3179. case WM_SETFOCUS:
  3180. /*
  3181. * wParam - handle of window that loses the input focus (may be NULL)
  3182. lParam - not used
  3183. */
  3184. MLSetFocus(ped);
  3185. break;
  3186. case WM_SIZE:
  3187. /*
  3188. * wParam - defines the type of resizing fullscreen, sizeiconic,
  3189. sizenormal etc.
  3190. lParam - new width in LOWORD, new height in HIGHWORD of client area
  3191. */
  3192. ECSize(ped, NULL, TRUE);
  3193. break;
  3194. case EM_FMTLINES:
  3195. /*
  3196. * wParam - indicates disposition of end-of-line chars. If non
  3197. * zero, the chars CR CR LF are placed at the end of a word
  3198. * wrapped line. If wParam is zero, the end of line chars are
  3199. * removed. This is only done when the user gets a handle (via
  3200. * EM_GETHANDLE) to the text. lParam - not used.
  3201. */
  3202. if (wParam)
  3203. MLInsertCrCrLf(ped);
  3204. else
  3205. MLStripCrCrLf(ped);
  3206. MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
  3207. return (LONG)(wParam != 0);
  3208. case EM_GETHANDLE:
  3209. /*
  3210. * wParam - not used
  3211. lParam - not used
  3212. */
  3213. /*
  3214. * Returns a handle to the edit control's text.
  3215. */
  3216. /*
  3217. * Null terminate the string. Note that we are guaranteed to have the
  3218. * memory for the NULL since ECInsertText allocates an extra
  3219. * WCHAR for the NULL terminator.
  3220. */
  3221. if (ped->fAnsi)
  3222. *(ECLock(ped) + ped->cch) = 0;
  3223. else
  3224. *((LPWSTR)ECLock(ped) + ped->cch) = 0;
  3225. ECUnlock(ped);
  3226. return ((LRESULT)ped->hText);
  3227. case EM_GETLINE:
  3228. /*
  3229. * wParam - line number to copy (0 is first line)
  3230. * lParam - buffer to copy text to. First WORD is max # of bytes to
  3231. * copy
  3232. */
  3233. return MLGetLine(ped, (ICH)wParam, (ICH)*(WORD UNALIGNED *)lParam, (LPSTR)lParam);
  3234. case EM_LINEFROMCHAR:
  3235. /*
  3236. * wParam - Contains the index value for the desired char in the text
  3237. * of the edit control. These are 0 based.
  3238. * lParam - not used
  3239. */
  3240. return (LRESULT)MLIchToLine(ped, (ICH)wParam);
  3241. case EM_LINEINDEX:
  3242. /*
  3243. * wParam - specifies the desired line number where the number of the
  3244. * first line is 0. If linenumber = 0, the line with the caret is used.
  3245. * lParam - not used.
  3246. * This function return s the number of character positions that occur
  3247. * preceeding the first char in a given line.
  3248. */
  3249. {
  3250. ICH ichResult = MLLineIndex(ped, (ICH)wParam);
  3251. if (ichResult == (ICH)-1) {
  3252. return -1;
  3253. }
  3254. return (LRESULT)ichResult;
  3255. }
  3256. break;
  3257. case EM_LINELENGTH:
  3258. /*
  3259. * wParam - specifies the character index of a character in the
  3260. specified line, where the first line is 0. If -1, the length
  3261. of the current line (with the caret) is return ed not including the
  3262. length of any selected text.
  3263. lParam - not used
  3264. */
  3265. return (LRESULT)MLLineLength(ped, (ICH)wParam);
  3266. case EM_LINESCROLL:
  3267. /*
  3268. * wParam - not used
  3269. lParam - Contains the number of lines and char positions to scroll
  3270. */
  3271. MLScroll(ped, TRUE, EM_LINESCROLL, (INT)lParam, TRUE);
  3272. MLScroll(ped, FALSE, EM_LINESCROLL, (INT)wParam, TRUE);
  3273. break;
  3274. case EM_REPLACESEL:
  3275. /*
  3276. * wParam - flag for 4.0+ apps saying whether to clear undo
  3277. lParam - Points to a null terminated replacement text.
  3278. */
  3279. MLReplaceSel(ped, (LPSTR)lParam);
  3280. if (!ped->f40Compat || !wParam)
  3281. ECEmptyUndo(Pundo(ped));
  3282. break;
  3283. case EM_SETHANDLE:
  3284. /*
  3285. * wParam - contains a handle to the text buffer
  3286. lParam - not used
  3287. */
  3288. MLSetHandle(ped, (HANDLE)wParam);
  3289. break;
  3290. case EM_SETRECT:
  3291. case EM_SETRECTNP:
  3292. //
  3293. // wParamLo -- not used
  3294. // lParam -- LPRECT with new formatting area
  3295. //
  3296. ECSize(ped, (LPRECT) lParam, (message != EM_SETRECTNP));
  3297. break;
  3298. case EM_SETSEL:
  3299. /*
  3300. * wParam - Under 3.1, specifies if we should scroll caret into
  3301. * view or not. 0 == scroll into view. 1 == don't scroll
  3302. * lParam - starting pos in lowword ending pos in high word
  3303. *
  3304. * Under Win32, wParam is the starting pos, lParam is the
  3305. * ending pos, and the caret is not scrolled into view.
  3306. * The message EM_SCROLLCARET forces the caret to be scrolled
  3307. * into view.
  3308. */
  3309. MLSetSelection(ped, TRUE, (ICH)wParam, (ICH)lParam);
  3310. break;
  3311. case EM_SCROLLCARET:
  3312. /*
  3313. * Scroll caret into view
  3314. */
  3315. MLEnsureCaretVisible(ped);
  3316. break;
  3317. case EM_GETFIRSTVISIBLELINE:
  3318. /*
  3319. * Returns the first visible line for multiline edit controls.
  3320. */
  3321. return (LONG)ped->ichScreenStart;
  3322. case WM_SYSKEYDOWN:
  3323. if (((WORD)wParam == VK_BACK) && ((DWORD)lParam & SYS_ALTERNATE)) {
  3324. SendMessage(ped->hwnd, EM_UNDO, 0, 0L);
  3325. break;
  3326. }
  3327. goto PassToDefaultWindowProc;
  3328. case WM_UNDO:
  3329. case EM_UNDO:
  3330. return MLUndo(ped);
  3331. case EM_SETTABSTOPS:
  3332. /*
  3333. * This sets the tab stop positions for multiline edit controls.
  3334. * wParam - Number of tab stops
  3335. * lParam - Far ptr to a UINT array containing the Tab stop positions
  3336. */
  3337. return MLSetTabStops(ped, (int)wParam, (LPINT)lParam);
  3338. case EM_POSFROMCHAR:
  3339. //
  3340. // wParam -- char index in text
  3341. // lParam -- not used
  3342. // This function returns the (x,y) position of the character
  3343. //
  3344. case EM_CHARFROMPOS:
  3345. //
  3346. // wParam -- unused
  3347. // lParam -- pt in client coordinates
  3348. // This function returns
  3349. // LOWORD: the position of the closest character
  3350. // to the passed in point. Beware of
  3351. // points not actually in the edit client...
  3352. // HIWORD: the index of the line the char is on
  3353. //
  3354. {
  3355. LONG xyPos;
  3356. LONG line;
  3357. hdc = ECGetEditDC(ped, TRUE);
  3358. if (message == EM_POSFROMCHAR) {
  3359. MLIchToXYPos(ped, hdc, (ICH)wParam, FALSE, &pt);
  3360. xyPos = MAKELONG(pt.x, pt.y);
  3361. } else {
  3362. POINTSTOPOINT(pt, lParam);
  3363. xyPos = MLMouseToIch(ped, hdc, &pt, &line);
  3364. xyPos = MAKELONG(xyPos, line);
  3365. }
  3366. ECReleaseEditDC(ped, hdc, TRUE);
  3367. return((LRESULT)xyPos);
  3368. break;
  3369. }
  3370. case WM_SETREDRAW:
  3371. DefWindowProcWorker(ped->pwnd, message, wParam, lParam, FALSE);
  3372. if (wParam) {
  3373. /*
  3374. * Backwards compatability hack needed so that winraid's edit
  3375. * controls work fine.
  3376. */
  3377. RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME);
  3378. }
  3379. break;
  3380. #if LATER
  3381. case WM_IME_ENDCOMPOSITION:
  3382. ECInOutReconversionMode(ped, FALSE);
  3383. break;
  3384. #endif
  3385. default:
  3386. PassToDefaultWindowProc:
  3387. return DefWindowProcWorker(ped->pwnd, message, wParam, lParam, ped->fAnsi);
  3388. }
  3389. return 1L;
  3390. } /* MLEditWndProc */
  3391. /***************************************************************************\
  3392. * MLDrawText AorW
  3393. *
  3394. * This function draws all the characters between ichStart and ichEnd for
  3395. * the given Multiline Edit Control.
  3396. *
  3397. * This function divides the block of text between ichStart and ichEnd
  3398. * into lines and each line into strips of text based on the selection
  3399. * attributes. It calls ECTabTheTextOut() to draw each strip.
  3400. * This takes care of the Negative A anc C widths of the current font, if
  3401. * it has any, on either side of each strip of text.
  3402. *
  3403. * NOTE: If the language pack is loaded the text is not divided into strips,
  3404. * nor is selection highlighting performed here. Whole lines are passed
  3405. * to the language pack to display with tab expansion and selection
  3406. * highlighting. (Since the language pack supports scripts with complex
  3407. * character re-ordering rules, only it can do this).
  3408. *
  3409. * History:
  3410. \***************************************************************************/
  3411. void MLDrawText(
  3412. PED ped,
  3413. HDC hdc,
  3414. ICH ichStart,
  3415. ICH ichEnd,
  3416. BOOL fSelChange)
  3417. {
  3418. DWORD textColorSave;
  3419. DWORD bkColorSave;
  3420. PSTR pText;
  3421. UINT wCurLine;
  3422. UINT wEndLine;
  3423. int xOffset;
  3424. ICH LengthToDraw;
  3425. ICH CurStripLength;
  3426. ICH ichAttrib, ichNewStart;
  3427. ICH ExtraLengthForNegA;
  3428. ICH ichT;
  3429. int iRemainingLengthInLine;
  3430. int xStPos, xClipStPos, xClipEndPos, yPos;
  3431. BOOL fFirstLineOfBlock = TRUE;
  3432. BOOL fDrawEndOfLineStrip = FALSE;
  3433. BOOL fDrawOnSameLine = FALSE;
  3434. BOOL fSelected = FALSE;
  3435. BOOL fLineBegins = FALSE;
  3436. STRIPINFO NegCInfo;
  3437. POINT pt;
  3438. //
  3439. // Just return if nothing to draw
  3440. if (!ped->ichLinesOnScreen)
  3441. return;
  3442. ECGetBrush(ped, hdc);
  3443. //
  3444. // Adjust the value of ichStart such that we need to draw only those lines
  3445. // visible on the screen.
  3446. //
  3447. if ((UINT)ichStart < (UINT)ped->chLines[ped->ichScreenStart]) {
  3448. ichStart = ped->chLines[ped->ichScreenStart];
  3449. if (ichStart > ichEnd)
  3450. return;
  3451. }
  3452. // Adjust the value of ichEnd such that we need to draw only those lines
  3453. // visible on the screen.
  3454. wCurLine = min(ped->ichScreenStart+ped->ichLinesOnScreen,ped->cLines-1);
  3455. ichT = ped->chLines[wCurLine] + MLLine(ped, wCurLine);
  3456. ichEnd = min(ichEnd, ichT);
  3457. wCurLine = MLIchToLine(ped, ichStart); // Starting line.
  3458. wEndLine = MLIchToLine(ped, ichEnd); // Ending line.
  3459. UserAssert(ped->chLines[wCurLine] <= ped->cch + 1);
  3460. UserAssert(ped->chLines[wEndLine] <= ped->cch + 1);
  3461. if (fSelChange && (GetBkMode(hdc) != OPAQUE))
  3462. {
  3463. /*
  3464. * if changing selection on a transparent edit control, just
  3465. * draw those lines from scratch
  3466. */
  3467. RECT rcStrip;
  3468. CopyRect(&rcStrip, &ped->rcFmt);
  3469. rcStrip.left -= ped->wLeftMargin;
  3470. if (ped->pLpkEditCallout) {
  3471. rcStrip.right += ped->wRightMargin;
  3472. }
  3473. rcStrip.top += (wCurLine - ped->ichScreenStart) * ped->lineHeight;
  3474. rcStrip.bottom = rcStrip.top + ((wEndLine - wCurLine) + 1) * ped->lineHeight;
  3475. NtUserInvalidateRect(ped->hwnd, &rcStrip, TRUE);
  3476. return;
  3477. }
  3478. // If it is either centered or right-justified, then draw the whole lines.
  3479. // Also draw whole lines if the language pack is handling line layout.
  3480. if ((ped->format != ES_LEFT) || (ped->pLpkEditCallout)) {
  3481. ichStart = ped->chLines[wCurLine];
  3482. ichEnd = ped->chLines[wEndLine] + MLLine(ped, wEndLine);
  3483. }
  3484. pText = ECLock(ped);
  3485. NtUserHideCaret(ped->hwnd);
  3486. //
  3487. // If ichStart stays on Second byte of DBCS, we have to
  3488. // adjust it. LiZ -- 5/5/93
  3489. //
  3490. if (ped->fAnsi && ped->fDBCS) {
  3491. ichStart = ECAdjustIch( ped, pText, ichStart );
  3492. }
  3493. UserAssert(ichStart <= ped->cch);
  3494. UserAssert(ichEnd <= ped->cch);
  3495. while (ichStart <= ichEnd) {
  3496. // Pass whole lines to the language pack to display with selection
  3497. // marking and tab expansion.
  3498. if (ped->pLpkEditCallout) {
  3499. ped->pLpkEditCallout->EditDrawText(
  3500. ped, hdc, pText + ped->cbChar*ichStart,
  3501. MLLine(ped, wCurLine),
  3502. (INT)ped->ichMinSel - (INT)ichStart, (INT)ped->ichMaxSel - (INT)ichStart,
  3503. MLIchToYPos(ped, ichStart, FALSE));
  3504. } else {
  3505. // xStPos: The starting Position where the string must be drawn.
  3506. // xClipStPos: The starting position for the clipping rect for the block.
  3507. // xClipEndPos: The ending position for the clipping rect for the block.
  3508. // Calculate the xyPos of starting point of the block.
  3509. MLIchToXYPos(ped, hdc, ichStart, FALSE, &pt);
  3510. xClipStPos = xStPos = pt.x;
  3511. yPos = pt.y;
  3512. // The attributes of the block is the same as that of ichStart.
  3513. ichAttrib = ichStart;
  3514. // If the current font has some negative C widths and if this is the
  3515. // begining of a block, we must start drawing some characters before the
  3516. // block to account for the negative C widths of the strip before the
  3517. // current strip; In this case, reset ichStart and xStPos.
  3518. if (fFirstLineOfBlock && ped->wMaxNegC) {
  3519. fFirstLineOfBlock = FALSE;
  3520. ichNewStart = max(((int)(ichStart - ped->wMaxNegCcharPos)), ((int)ped->chLines[wCurLine]));
  3521. // If ichStart needs to be changed, then change xStPos also accordingly.
  3522. if (ichNewStart != ichStart) {
  3523. if (ped->fAnsi && ped->fDBCS) {
  3524. //
  3525. // Adjust DBCS alignment...
  3526. //
  3527. ichNewStart = ECAdjustIchNext( ped, pText, ichNewStart );
  3528. }
  3529. MLIchToXYPos(ped, hdc, ichStart = ichNewStart, FALSE, &pt);
  3530. xStPos = pt.x;
  3531. }
  3532. }
  3533. // Calc the number of characters remaining to be drawn in the current line.
  3534. iRemainingLengthInLine = MLLine(ped, wCurLine) -
  3535. (ichStart - ped->chLines[wCurLine]);
  3536. // If this is the last line of a block, we may not have to draw all the
  3537. // remaining lines; We must draw only upto ichEnd.
  3538. if (wCurLine == wEndLine)
  3539. LengthToDraw = ichEnd - ichStart;
  3540. else
  3541. LengthToDraw = iRemainingLengthInLine;
  3542. // Find out how many pixels we indent the line for non-left-justified
  3543. // formats
  3544. if (ped->format != ES_LEFT)
  3545. xOffset = MLCalcXOffset(ped, hdc, wCurLine);
  3546. else
  3547. xOffset = -((int)(ped->xOffset));
  3548. // Check if this is the begining of a line.
  3549. if (ichAttrib == ped->chLines[wCurLine]) {
  3550. fLineBegins = TRUE;
  3551. xClipStPos = ped->rcFmt.left - ped->wLeftMargin;
  3552. }
  3553. //
  3554. // The following loop divides this 'wCurLine' into strips based on the
  3555. // selection attributes and draw them strip by strip.
  3556. do {
  3557. //
  3558. // If ichStart is pointing at CRLF or CRCRLF, then iRemainingLength
  3559. // could have become negative because MLLine does not include
  3560. // CR and LF at the end of a line.
  3561. //
  3562. if (iRemainingLengthInLine < 0) // If Current line is completed,
  3563. break; // go on to the next line.
  3564. //
  3565. // Check if a part of the block is selected and if we need to
  3566. // show it with a different attribute.
  3567. //
  3568. if (!(ped->ichMinSel == ped->ichMaxSel ||
  3569. ichAttrib >= ped->ichMaxSel ||
  3570. ichEnd < ped->ichMinSel ||
  3571. (!ped->fNoHideSel && !ped->fFocus))) {
  3572. //
  3573. // OK! There is a selection somewhere in this block!
  3574. // Check if this strip has selection attribute.
  3575. //
  3576. if (ichAttrib < ped->ichMinSel) {
  3577. fSelected = FALSE; // This strip is not selected
  3578. // Calculate the length of this strip with normal attribute.
  3579. CurStripLength = min(ichStart+LengthToDraw, ped->ichMinSel)-ichStart;
  3580. fLineBegins = FALSE;
  3581. } else {
  3582. // The current strip has the selection attribute.
  3583. if (fLineBegins) { // Is it the first part of a line?
  3584. // Then, draw the left margin area with normal attribute.
  3585. fSelected = FALSE;
  3586. CurStripLength = 0;
  3587. xClipStPos = ped->rcFmt.left - ped->wLeftMargin;
  3588. fLineBegins = FALSE;
  3589. } else {
  3590. // Else, draw the strip with selection attribute.
  3591. fSelected = TRUE;
  3592. CurStripLength = min(ichStart+LengthToDraw, ped->ichMaxSel)-ichStart;
  3593. // Select in the highlight colors.
  3594. bkColorSave = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  3595. if (!ped->fDisabled)
  3596. textColorSave = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  3597. }
  3598. }
  3599. } else {
  3600. // The whole strip has no selection attributes.
  3601. CurStripLength = LengthToDraw;
  3602. }
  3603. //
  3604. // Other than the current strip, do we still have anything
  3605. // left to be drawn in the current line?
  3606. //
  3607. fDrawOnSameLine = (LengthToDraw != CurStripLength);
  3608. //
  3609. // When we draw this strip, we need to draw some more characters
  3610. // beyond the end of this strip to account for the negative A
  3611. // widths of the characters that follow this strip.
  3612. //
  3613. ExtraLengthForNegA = min(iRemainingLengthInLine-CurStripLength, ped->wMaxNegAcharPos);
  3614. //
  3615. // The blank strip at the end of the line needs to be drawn with
  3616. // normal attribute irrespective of whether the line has selection
  3617. // attribute or not. Hence, if the last strip of the line has selection
  3618. // attribute, then this blank strip needs to be drawn separately.
  3619. // Else, we can draw the blank strip along with the last strip.
  3620. //
  3621. // Is this the last strip of the current line?
  3622. if (iRemainingLengthInLine == (int)CurStripLength) {
  3623. if (fSelected) { // Does this strip have selection attribute?
  3624. // Then we need to draw the end of line strip separately.
  3625. fDrawEndOfLineStrip = TRUE; // Draw the end of line strip.
  3626. MLIchToXYPos(ped, hdc, ichStart+CurStripLength, TRUE, &pt);
  3627. xClipEndPos = pt.x;
  3628. } else {
  3629. //
  3630. // Set the xClipEndPos to a big value sothat the blank
  3631. // strip will be drawn automatically when the last strip
  3632. // is drawn.
  3633. //
  3634. xClipEndPos = MAXCLIPENDPOS;
  3635. }
  3636. } else {
  3637. //
  3638. // This is not the last strip of this line; So, set the ending
  3639. // clip position accurately.
  3640. //
  3641. MLIchToXYPos(ped, hdc, ichStart+CurStripLength, FALSE, &pt);
  3642. xClipEndPos = pt.x;
  3643. }
  3644. //
  3645. // Draw the current strip starting from xStPos, clipped to the area
  3646. // between xClipStPos and xClipEndPos. Obtain "NegCInfo" and use it
  3647. // in drawing the next strip.
  3648. //
  3649. ECTabTheTextOut(hdc, xClipStPos, xClipEndPos,
  3650. xStPos, yPos, (LPSTR)(pText+ichStart*ped->cbChar),
  3651. CurStripLength+ExtraLengthForNegA, ichStart, ped,
  3652. ped->rcFmt.left+xOffset, fSelected ? ECT_SELECTED : ECT_NORMAL, &NegCInfo);
  3653. if (fSelected) {
  3654. //
  3655. // If this strip was selected, then the next strip won't have
  3656. // selection attribute
  3657. //
  3658. fSelected = FALSE;
  3659. SetBkColor(hdc, bkColorSave);
  3660. if (!ped->fDisabled)
  3661. SetTextColor(hdc, textColorSave);
  3662. }
  3663. // Do we have one more strip to draw on the current line?
  3664. if (fDrawOnSameLine || fDrawEndOfLineStrip) {
  3665. int iLastDrawnLength;
  3666. //
  3667. // Next strip's attribute is decided based on the char at ichAttrib
  3668. //
  3669. ichAttrib = ichStart + CurStripLength;
  3670. //
  3671. // When drawing the next strip, start at a few chars before
  3672. // the actual start to account for the Neg 'C' of the strip
  3673. // just drawn.
  3674. //
  3675. iLastDrawnLength = CurStripLength +ExtraLengthForNegA - NegCInfo.nCount;
  3676. //
  3677. // Adjust DBCS alignment...
  3678. //
  3679. if (ped->fAnsi && ped->fDBCS) {
  3680. ichNewStart = ECAdjustIch(ped,pText,ichStart+iLastDrawnLength);
  3681. iLastDrawnLength = ichNewStart - ichStart;
  3682. ichStart = ichNewStart;
  3683. } else {
  3684. ichStart += iLastDrawnLength;
  3685. }
  3686. LengthToDraw -= iLastDrawnLength;
  3687. iRemainingLengthInLine -= iLastDrawnLength;
  3688. //
  3689. // The start of clip rect for the next strip.
  3690. //
  3691. xStPos = NegCInfo.XStartPos;
  3692. xClipStPos = xClipEndPos;
  3693. }
  3694. // Draw the blank strip at the end of line seperately, if required.
  3695. if (fDrawEndOfLineStrip) {
  3696. ECTabTheTextOut(hdc, xClipStPos, MAXCLIPENDPOS, xStPos, yPos,
  3697. (LPSTR)(pText+ichStart*ped->cbChar), LengthToDraw, ichStart,
  3698. ped, ped->rcFmt.left+xOffset, ECT_NORMAL, &NegCInfo);
  3699. fDrawEndOfLineStrip = FALSE;
  3700. }
  3701. }
  3702. while(fDrawOnSameLine); // do while loop ends here.
  3703. }
  3704. // Let us move on to the next line of this block to be drawn.
  3705. wCurLine++;
  3706. if (ped->cLines > wCurLine)
  3707. ichStart = ped->chLines[wCurLine];
  3708. else
  3709. ichStart = ichEnd+1; // We have reached the end of the text.
  3710. } // while loop ends here
  3711. ECUnlock(ped);
  3712. NtUserShowCaret(ped->hwnd);
  3713. MLSetCaretPosition(ped, hdc);
  3714. }