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.

609 lines
19 KiB

  1. //// DspLogcl - Display logical text
  2. //
  3. // Shows logical characters and selection range in backing store order and fixed width.
  4. #include "precomp.hxx"
  5. #include "global.h"
  6. //// DottedLine
  7. //
  8. // Draws a horizontal or a vertical dotted line
  9. //
  10. // Not the best algorithm.
  11. void DottedLine(HDC hdc, int x, int y, int dx, int dy) {
  12. SetPixel(hdc, x, y, 0);
  13. if (dx) {
  14. // Horizontal line
  15. while (dx > 2) {
  16. x += 3;
  17. SetPixel(hdc, x, y, 0);
  18. dx -= 3;
  19. }
  20. x += dx;
  21. SetPixel(hdc, x, y, 0);
  22. } else {
  23. // Vertical line
  24. while (dy > 2) {
  25. y += 3;
  26. SetPixel(hdc, x, y, 0);
  27. dy -= 3;
  28. }
  29. y += dy;
  30. SetPixel(hdc, x, y, 0);
  31. }
  32. }
  33. //// PaintLogical - show characters in logical sequence
  34. //
  35. // Display each glyph separately - override the default advance width
  36. // processing to defeat any overlapping or combining action that the
  37. // font performs with it's default ABC width.
  38. //
  39. // To achieve this, we call ScriptGetGlyphABCWidth to obtain the
  40. // leading side bearing (A), the black box width (B) and the trailing
  41. // side bearing (C).
  42. //
  43. // Since we can control only the advance width per glyph, we have to
  44. // calulate suitable advance widths to override the affect of the
  45. // ABC values in the font.
  46. //
  47. // You should never normally need to call ScriptGetGlyphABCWidth.
  48. //
  49. // PaintLogical has to implement a form of font fallback - Indian and
  50. // Tamil scripts are not present in Tahoma, so we go
  51. // directly to Mangal and Latha for characters in those Unicode ranges.
  52. void PaintLogical(
  53. HDC hdc,
  54. int *piY,
  55. RECT *prc,
  56. int iLineHeight) {
  57. const int MAXBUF = 100;
  58. const int CELLGAP = 4; // Pixels between adjacent glyphs
  59. int icpLineStart; // First character of line
  60. int icpLineEnd; // End of line (end of buffer or index of CR character)
  61. int icp;
  62. int iLen;
  63. int iPartLen; // Part of string in a single font
  64. int iPartX;
  65. int iPartWidth;
  66. WORD wGlyphBuf[MAXBUF];
  67. int idx[MAXBUF]; // Force widths so all characters show
  68. BYTE bFont[MAXBUF]; // Font used for each character
  69. ABC abc[MAXBUF];
  70. int iTotX;
  71. int ildx; // Overall line dx, adjusts for 'A' width of leading glyph
  72. int iSliderX;
  73. int iFont; // 0 = Tahoma, 1 = Mangal, 2 = Latha
  74. RECT rcClear; // Clear each line before displaying it
  75. // Selection highlighting
  76. bool bHighlight; // Current state of highlighting in the hdc
  77. int iFrom; // Selection range
  78. int iTo;
  79. DWORD dwOldBkColor=0;
  80. DWORD dwOldTextColor=0;
  81. // Item analysis
  82. SCRIPT_ITEM items[MAXBUF];
  83. SCRIPT_CONTROL scriptControl;
  84. SCRIPT_STATE scriptState;
  85. INT iItem;
  86. #define NUMLOGICALFONTS 4
  87. SCRIPT_CACHE sc[NUMLOGICALFONTS];
  88. HFONT hf[NUMLOGICALFONTS];
  89. HFONT hfold;
  90. HRESULT hr;
  91. SCRIPT_FONTPROPERTIES sfp;
  92. BOOL bMissing;
  93. icpLineStart = 0;
  94. hf[0] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Tahoma");
  95. hf[1] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Mangal");
  96. hf[2] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Latha");
  97. hf[3] = CreateFontA(iLineHeight*7/20, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Tahoma"); // for bidi level digits
  98. iFont = 0;
  99. hfold = (HFONT) SelectObject(hdc, hf[iFont]);
  100. ildx = 0;
  101. memset(sc, 0, sizeof(sc));
  102. bHighlight = FALSE;
  103. INT iSliderHeight = g_fOverrideDx ? iLineHeight * 5 / 10 : 0;
  104. INT iLevelsHeight = g_fShowLevels ? iLineHeight * 8 / 20 : 0;
  105. // Display line by line
  106. while (icpLineStart < g_iTextLen) {
  107. // Clear line before displaying it
  108. rcClear = *prc;
  109. rcClear.top = *piY;
  110. rcClear.bottom = *piY + iLineHeight + iSliderHeight + iLevelsHeight;
  111. FillRect(hdc, &rcClear, (HBRUSH) GetStockObject(WHITE_BRUSH));
  112. // Find end of line or end of buffer
  113. icpLineEnd = icpLineStart;
  114. while (icpLineEnd < g_iTextLen && g_wcBuf[icpLineEnd] != 0x0D) {
  115. icpLineEnd++;
  116. }
  117. if (icpLineEnd - icpLineStart > MAXBUF) {
  118. iLen = MAXBUF;
  119. } else {
  120. iLen = icpLineEnd - icpLineStart;
  121. }
  122. // Obtain item analysis
  123. scriptControl = g_ScriptControl;
  124. scriptState = g_ScriptState;
  125. ScriptItemize(g_wcBuf+icpLineStart, iLen, MAXBUF, &scriptControl, &scriptState, items, NULL);
  126. // Determine font and glyph index for each codepoint
  127. if (iFont != 0) { // Start with Tahoma
  128. iFont = 0;
  129. SelectObject(hdc, hf[0]);
  130. }
  131. hr = ScriptGetCMap(hdc, &sc[iFont], g_wcBuf+icpLineStart, iLen, 0, wGlyphBuf);
  132. if (SUCCEEDED(hr))
  133. {
  134. memset(bFont, 0, iLen);
  135. if (hr != S_OK) {
  136. // Some characters were not in Tahoma
  137. sfp.cBytes = sizeof(sfp);
  138. ScriptGetFontProperties(hdc, &sc[iFont], &sfp);
  139. bMissing = FALSE;
  140. for (icp=0; icp<iLen; icp++) {
  141. if (wGlyphBuf[icp] == sfp.wgDefault) {
  142. bFont[icp] = 1;
  143. bMissing = TRUE;
  144. }
  145. }
  146. // Try other fonts
  147. while (bMissing && iFont < 2) {
  148. iFont++;
  149. SelectObject(hdc, hf[iFont]);
  150. ScriptGetFontProperties(hdc, &sc[iFont], &sfp);
  151. bMissing = FALSE;
  152. for (icp=0; icp<iLen; icp++) {
  153. if (bFont[icp] == iFont) {
  154. ScriptGetCMap(hdc, &sc[iFont], g_wcBuf+icpLineStart+icp, 1, 0, wGlyphBuf+icp);
  155. if (wGlyphBuf[icp] == sfp.wgDefault) {
  156. bFont[icp] = (BYTE)(iFont+1);
  157. bMissing = TRUE;
  158. }
  159. }
  160. }
  161. }
  162. if (bMissing) {
  163. // Remaining missing characters come from font 0
  164. for (icp=0; icp<iLen; icp++) {
  165. if (bFont[icp] >= NUMLOGICALFONTS) {
  166. bFont[icp] = 0;
  167. }
  168. }
  169. }
  170. }
  171. // Display each glyphs black box next to the previous. Override the
  172. // default ABC behaviour.
  173. idx[0] = 0;
  174. for (icp=0; icp<iLen; icp++) {
  175. if (iFont != bFont[icp]) {
  176. iFont = bFont[icp];
  177. SelectObject(hdc, hf[iFont]);
  178. }
  179. ScriptGetGlyphABCWidth(hdc, &sc[iFont], wGlyphBuf[icp], &abc[icp]);
  180. if (g_wcBuf[icpLineStart+icp] == ' ') {
  181. // Treat entire space as black
  182. abc[icp].abcB += abc[icp].abcA; abc[icp].abcA = 0;
  183. abc[icp].abcB += abc[icp].abcC; abc[icp].abcC = 0;
  184. }
  185. // Glyph black box width is abc.abcB
  186. // We'd like the glyph to appear 2 pixels to the right of the
  187. // previous glyph.
  188. //
  189. // The default placement of left edge is abc.abcA.
  190. //
  191. // Therefore we need to shift this character to the right by
  192. // 2 - abc.abcA to get it positioned correctly. We do this by
  193. // updating the advance width for the previous character.
  194. if (!icp) {
  195. ildx = CELLGAP/2 - abc[icp].abcA;
  196. } else {
  197. idx[icp-1] += CELLGAP - abc[icp].abcA;
  198. }
  199. // Now adjust the advance width for this character to take us to
  200. // the right edge of it's black box.
  201. idx[icp] = abc[icp].abcB + abc[icp].abcA;
  202. }
  203. // Support selection range specified in either direction
  204. if (g_iFrom <= g_iTo) {
  205. iFrom = g_iFrom - icpLineStart;
  206. iTo = g_iTo - icpLineStart;
  207. } else {
  208. iFrom = g_iTo - icpLineStart;
  209. iTo = g_iFrom - icpLineStart;
  210. }
  211. // Display glyphs in their appropriate fonts
  212. icp = 0;
  213. iPartX = prc->left+ildx;
  214. while (icp < iLen) {
  215. if (iFont != bFont[icp]) {
  216. iFont = bFont[icp];
  217. SelectObject(hdc, hf[iFont]);
  218. }
  219. // Set selection highlighting at start
  220. if ( icp >= iFrom
  221. && icp < iTo
  222. && !bHighlight) {
  223. // Turn on highlighting
  224. dwOldBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  225. dwOldTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  226. bHighlight = TRUE;
  227. } else if ( ( icp < iFrom
  228. || icp >= iTo)
  229. && bHighlight) {
  230. // Turn off highlighting
  231. SetBkColor(hdc, dwOldBkColor);
  232. SetTextColor(hdc, dwOldTextColor);
  233. bHighlight = FALSE;
  234. }
  235. // Find longest run from a single font, and
  236. // without change of highlighting
  237. iPartLen = 0;
  238. iPartWidth = 0;
  239. while ( icp+iPartLen < iLen
  240. && iFont == bFont[icp+iPartLen]
  241. && bHighlight == (icp+iPartLen >= iFrom && icp+iPartLen < iTo)) {
  242. iPartWidth += idx[icp+iPartLen];
  243. iPartLen++;
  244. }
  245. // Display single font, single highlighting
  246. ExtTextOutW(hdc,
  247. iPartX,
  248. *piY+2,
  249. ETO_CLIPPED | ETO_GLYPH_INDEX,
  250. prc,
  251. wGlyphBuf+icp,
  252. iPartLen,
  253. idx+icp);
  254. icp += iPartLen;
  255. iPartX += iPartWidth;
  256. }
  257. // Mark the cells to make the characters stand out clearly
  258. MoveToEx(hdc, prc->left, *piY, NULL);
  259. LineTo(hdc, prc->left, *piY + iLineHeight*3/4);
  260. iTotX = 0;
  261. for (icp=0; icp<iLen; icp++){
  262. iTotX += abc[icp].abcB + CELLGAP;
  263. idx[icp] = iTotX; // Record cell position for mouse hit testing
  264. DottedLine(hdc, prc->left + iTotX, *piY, 0, iLineHeight*3/4);
  265. // Add slider for OverridedDx control
  266. if (g_fOverrideDx) {
  267. iSliderX = prc->left + (icp==0 ? idx[0]/2 : (idx[icp-1] + idx[icp])/2);
  268. // Draw the axis of the slider
  269. DottedLine(hdc, iSliderX, *piY + iLineHeight*35/40, 0, iSliderHeight*35/40);
  270. // Draw the knob
  271. if (g_iWidthBuf[icpLineStart + icp] < iSliderHeight) {
  272. MoveToEx(hdc, iSliderX-2, *piY + iLineHeight*35/40 + iSliderHeight*35/40 - g_iWidthBuf[icpLineStart + icp], NULL);
  273. LineTo (hdc, iSliderX+3, *piY + iLineHeight*35/40 + iSliderHeight*35/40 - g_iWidthBuf[icpLineStart + icp]);
  274. } else {
  275. MoveToEx(hdc, iSliderX-2, *piY + iLineHeight*35/40, NULL);
  276. LineTo (hdc, iSliderX+3, *piY + iLineHeight*35/40);
  277. }
  278. }
  279. }
  280. MoveToEx(hdc, prc->left + iTotX, *piY, NULL);
  281. LineTo(hdc, prc->left + iTotX, *piY + iLineHeight*30/40);
  282. MoveToEx(hdc, prc->left, *piY, NULL);
  283. LineTo(hdc, prc->left + iTotX, *piY);
  284. MoveToEx(hdc, prc->left, *piY + iLineHeight*30/40, NULL);
  285. LineTo(hdc, prc->left + iTotX, *piY + iLineHeight*30/40);
  286. if (g_fShowLevels)
  287. {
  288. // Display bidi levels for each codepoint
  289. iItem = 0;
  290. iFont = 3;
  291. SelectObject(hdc, hf[3]);
  292. for (icp=0; icp<iLen; icp++)
  293. {
  294. if (icp == items[iItem+1].iCharPos)
  295. {
  296. iItem++;
  297. // Draw a vertical line to mark the item boundary
  298. MoveToEx(hdc, prc->left + idx[icp-1], *piY + iLineHeight*35/40 + iSliderHeight, NULL);
  299. LineTo( hdc, prc->left + idx[icp-1], *piY + iLineHeight*35/40 + iSliderHeight + iLevelsHeight*35/40);
  300. }
  301. // Establish where horizontally to display the digit
  302. char chDigit = char('0' + items[iItem].a.s.uBidiLevel);
  303. int digitWidth;
  304. GetCharWidth32A(hdc, chDigit, chDigit, &digitWidth);
  305. ExtTextOutA(
  306. hdc,
  307. prc->left + (icp==0 ? idx[0]/2 : (idx[icp-1] + idx[icp])/2) - digitWidth / 2,
  308. *piY + iLineHeight*35/40 + iSliderHeight,
  309. 0,
  310. NULL,
  311. &chDigit,
  312. 1,
  313. NULL);
  314. }
  315. }
  316. // Check whether mouse clicks in this line are waiting to be processed
  317. if ( g_fOverrideDx
  318. && g_fMouseUp && g_iMouseUpY > *piY + iLineHeight*33/40 && g_iMouseUpY < *piY + iLineHeight*63/40) {
  319. // Procss change to DX override slider
  320. icp = 0;
  321. while (icp<iLen && prc->left + idx[icp] < g_iMouseUpX) {
  322. icp++;
  323. }
  324. g_iWidthBuf[icpLineStart+icp] = *piY + 60 - g_iMouseUpY; // Adjust this slider
  325. InvalidateText(); // Force slider to redraw at new position
  326. g_fMouseDown = FALSE;
  327. g_fMouseUp = FALSE;
  328. g_iFrom = icpLineStart+icp;
  329. g_iTo = icpLineStart+icp;
  330. } else if (g_fMouseDown && g_iMouseDownY > *piY && g_iMouseDownY < *piY+iLineHeight) {
  331. // Handle text selection
  332. // Record char pos at left button down
  333. // Snap mouse hit to closest character boundary
  334. if (g_iMouseDownX < prc->left + idx[0]/2) {
  335. icp = 0;
  336. } else {
  337. icp = 1;
  338. while ( icp < iLen
  339. && g_iMouseDownX > prc->left + (idx[icp-1] + idx[icp]) / 2) {
  340. icp++;
  341. }
  342. }
  343. g_iFrom = icp + icpLineStart;
  344. if (g_iFrom < icpLineStart) {
  345. g_iFrom = icpLineStart;
  346. }
  347. if (g_iFrom > icpLineEnd) {
  348. g_iFrom = icpLineEnd;
  349. }
  350. g_fMouseDown = FALSE;
  351. }
  352. if (g_fMouseUp && g_iMouseUpY > *piY && g_iMouseUpY < *piY+iLineHeight) {
  353. // Complete selection processing
  354. if (g_iMouseUpX < prc->left + idx[0]/2) {
  355. icp = 0;
  356. } else {
  357. icp = 1;
  358. while ( icp < iLen
  359. && g_iMouseUpX > prc->left + (idx[icp-1] + idx[icp]) / 2) {
  360. icp++;
  361. }
  362. }
  363. g_iTo = icp + icpLineStart;
  364. if (g_iTo < icpLineStart) {
  365. g_iTo = icpLineStart;
  366. }
  367. if (g_iTo > icpLineEnd) {
  368. g_iTo = icpLineEnd;
  369. }
  370. // Caret is where mouse was raised
  371. g_iCurChar = g_iTo;
  372. g_iCaretSection = CARET_SECTION_LOGICAL; // Show caret in logical text
  373. g_fUpdateCaret = TRUE;
  374. g_fMouseUp = FALSE; // Signal that the mouse up is processed
  375. }
  376. if ( g_fUpdateCaret
  377. && g_iCurChar >= icpLineStart
  378. && g_iCurChar <= icpLineEnd
  379. && g_iCaretSection == CARET_SECTION_LOGICAL) {
  380. g_fUpdateCaret = FALSE;
  381. if (g_iCurChar <= icpLineStart) {
  382. ResetCaret(prc->left, *piY, iLineHeight);
  383. } else {
  384. ResetCaret(prc->left + idx[g_iCurChar - icpLineStart - 1], *piY, iLineHeight);
  385. }
  386. }
  387. }
  388. else {
  389. // ScriptGetCMap failed - therefore this is not a glyphable font.
  390. // This could indicate
  391. // A printer device font
  392. // We're running on FE Win95 which cannot handle glyph indices
  393. //
  394. // For the sample app, we know we are using a glyphable Truetype font
  395. // on a screen DC, so it must mean the sample is running on a Far
  396. // East version of Windows 95.
  397. // Theoretically we could go to the trouble of calling
  398. // WideCharToMultiByte and using the 'A' char interfaces to
  399. // implement DspLogcl.
  400. // However this is only a sample program - DspPlain and DspFormt
  401. // work correctly, but there's no advantage in implementing
  402. // DspLogcl so well.
  403. // Display an apology.
  404. ExtTextOutA(hdc, prc->left+2, *piY+2, ETO_CLIPPED, prc, "Sorry, no logical text display on Far East Windows 95.", 54, NULL);
  405. icpLineEnd = g_iTextLen; // Hack to stop display of subsequent lines
  406. }
  407. *piY += iLineHeight + iSliderHeight + iLevelsHeight;
  408. // Advance to next line
  409. if (g_fPresentation) {
  410. icpLineStart = g_iTextLen; // Only show one line in presentation mode
  411. } else {
  412. if (icpLineEnd < g_iTextLen) {
  413. icpLineEnd++;
  414. }
  415. if (icpLineEnd < g_iTextLen && g_wcBuf[icpLineEnd] == 0x0A) {
  416. icpLineEnd++;
  417. }
  418. icpLineStart = icpLineEnd;
  419. }
  420. }
  421. SelectObject(hdc, hfold);
  422. if (bHighlight) {
  423. // Turn off highlighting
  424. SetBkColor(hdc, dwOldBkColor);
  425. SetTextColor(hdc, dwOldTextColor);
  426. bHighlight = FALSE;
  427. }
  428. for (iFont=0; iFont<NUMLOGICALFONTS; iFont++) {
  429. DeleteObject(hf[iFont]);
  430. if (sc[iFont]) {
  431. ScriptFreeCache(&sc[iFont]);
  432. }
  433. }
  434. }