//// DspLogcl - Display logical text // // Shows logical characters and selection range in backing store order and fixed width. #include "precomp.hxx" #include "global.h" //// DottedLine // // Draws a horizontal or a vertical dotted line // // Not the best algorithm. void DottedLine(HDC hdc, int x, int y, int dx, int dy) { SetPixel(hdc, x, y, 0); if (dx) { // Horizontal line while (dx > 2) { x += 3; SetPixel(hdc, x, y, 0); dx -= 3; } x += dx; SetPixel(hdc, x, y, 0); } else { // Vertical line while (dy > 2) { y += 3; SetPixel(hdc, x, y, 0); dy -= 3; } y += dy; SetPixel(hdc, x, y, 0); } } //// PaintLogical - show characters in logical sequence // // Display each glyph separately - override the default advance width // processing to defeat any overlapping or combining action that the // font performs with it's default ABC width. // // To achieve this, we call ScriptGetGlyphABCWidth to obtain the // leading side bearing (A), the black box width (B) and the trailing // side bearing (C). // // Since we can control only the advance width per glyph, we have to // calulate suitable advance widths to override the affect of the // ABC values in the font. // // You should never normally need to call ScriptGetGlyphABCWidth. // // PaintLogical has to implement a form of font fallback - Indian and // Tamil scripts are not present in Tahoma, so we go // directly to Mangal and Latha for characters in those Unicode ranges. void PaintLogical( HDC hdc, int *piY, RECT *prc, int iLineHeight) { const int MAXBUF = 100; const int CELLGAP = 4; // Pixels between adjacent glyphs int icpLineStart; // First character of line int icpLineEnd; // End of line (end of buffer or index of CR character) int icp; int iLen; int iPartLen; // Part of string in a single font int iPartX; int iPartWidth; WORD wGlyphBuf[MAXBUF]; int idx[MAXBUF]; // Force widths so all characters show BYTE bFont[MAXBUF]; // Font used for each character ABC abc[MAXBUF]; int iTotX; int ildx; // Overall line dx, adjusts for 'A' width of leading glyph int iSliderX; int iFont; // 0 = Tahoma, 1 = Mangal, 2 = Latha RECT rcClear; // Clear each line before displaying it // Selection highlighting bool bHighlight; // Current state of highlighting in the hdc int iFrom; // Selection range int iTo; DWORD dwOldBkColor=0; DWORD dwOldTextColor=0; // Item analysis SCRIPT_ITEM items[MAXBUF]; SCRIPT_CONTROL scriptControl; SCRIPT_STATE scriptState; INT iItem; #define NUMLOGICALFONTS 4 SCRIPT_CACHE sc[NUMLOGICALFONTS]; HFONT hf[NUMLOGICALFONTS]; HFONT hfold; HRESULT hr; SCRIPT_FONTPROPERTIES sfp; BOOL bMissing; icpLineStart = 0; hf[0] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Tahoma"); hf[1] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Mangal"); hf[2] = CreateFontA(iLineHeight*7/10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Latha"); hf[3] = CreateFontA(iLineHeight*7/20, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "Tahoma"); // for bidi level digits iFont = 0; hfold = (HFONT) SelectObject(hdc, hf[iFont]); ildx = 0; memset(sc, 0, sizeof(sc)); bHighlight = FALSE; INT iSliderHeight = g_fOverrideDx ? iLineHeight * 5 / 10 : 0; INT iLevelsHeight = g_fShowLevels ? iLineHeight * 8 / 20 : 0; // Display line by line while (icpLineStart < g_iTextLen) { // Clear line before displaying it rcClear = *prc; rcClear.top = *piY; rcClear.bottom = *piY + iLineHeight + iSliderHeight + iLevelsHeight; FillRect(hdc, &rcClear, (HBRUSH) GetStockObject(WHITE_BRUSH)); // Find end of line or end of buffer icpLineEnd = icpLineStart; while (icpLineEnd < g_iTextLen && g_wcBuf[icpLineEnd] != 0x0D) { icpLineEnd++; } if (icpLineEnd - icpLineStart > MAXBUF) { iLen = MAXBUF; } else { iLen = icpLineEnd - icpLineStart; } // Obtain item analysis scriptControl = g_ScriptControl; scriptState = g_ScriptState; ScriptItemize(g_wcBuf+icpLineStart, iLen, MAXBUF, &scriptControl, &scriptState, items, NULL); // Determine font and glyph index for each codepoint if (iFont != 0) { // Start with Tahoma iFont = 0; SelectObject(hdc, hf[0]); } hr = ScriptGetCMap(hdc, &sc[iFont], g_wcBuf+icpLineStart, iLen, 0, wGlyphBuf); if (SUCCEEDED(hr)) { memset(bFont, 0, iLen); if (hr != S_OK) { // Some characters were not in Tahoma sfp.cBytes = sizeof(sfp); ScriptGetFontProperties(hdc, &sc[iFont], &sfp); bMissing = FALSE; for (icp=0; icp= NUMLOGICALFONTS) { bFont[icp] = 0; } } } } // Display each glyphs black box next to the previous. Override the // default ABC behaviour. idx[0] = 0; for (icp=0; icpleft+ildx; while (icp < iLen) { if (iFont != bFont[icp]) { iFont = bFont[icp]; SelectObject(hdc, hf[iFont]); } // Set selection highlighting at start if ( icp >= iFrom && icp < iTo && !bHighlight) { // Turn on highlighting dwOldBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); dwOldTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); bHighlight = TRUE; } else if ( ( icp < iFrom || icp >= iTo) && bHighlight) { // Turn off highlighting SetBkColor(hdc, dwOldBkColor); SetTextColor(hdc, dwOldTextColor); bHighlight = FALSE; } // Find longest run from a single font, and // without change of highlighting iPartLen = 0; iPartWidth = 0; while ( icp+iPartLen < iLen && iFont == bFont[icp+iPartLen] && bHighlight == (icp+iPartLen >= iFrom && icp+iPartLen < iTo)) { iPartWidth += idx[icp+iPartLen]; iPartLen++; } // Display single font, single highlighting ExtTextOutW(hdc, iPartX, *piY+2, ETO_CLIPPED | ETO_GLYPH_INDEX, prc, wGlyphBuf+icp, iPartLen, idx+icp); icp += iPartLen; iPartX += iPartWidth; } // Mark the cells to make the characters stand out clearly MoveToEx(hdc, prc->left, *piY, NULL); LineTo(hdc, prc->left, *piY + iLineHeight*3/4); iTotX = 0; for (icp=0; icpleft + iTotX, *piY, 0, iLineHeight*3/4); // Add slider for OverridedDx control if (g_fOverrideDx) { iSliderX = prc->left + (icp==0 ? idx[0]/2 : (idx[icp-1] + idx[icp])/2); // Draw the axis of the slider DottedLine(hdc, iSliderX, *piY + iLineHeight*35/40, 0, iSliderHeight*35/40); // Draw the knob if (g_iWidthBuf[icpLineStart + icp] < iSliderHeight) { MoveToEx(hdc, iSliderX-2, *piY + iLineHeight*35/40 + iSliderHeight*35/40 - g_iWidthBuf[icpLineStart + icp], NULL); LineTo (hdc, iSliderX+3, *piY + iLineHeight*35/40 + iSliderHeight*35/40 - g_iWidthBuf[icpLineStart + icp]); } else { MoveToEx(hdc, iSliderX-2, *piY + iLineHeight*35/40, NULL); LineTo (hdc, iSliderX+3, *piY + iLineHeight*35/40); } } } MoveToEx(hdc, prc->left + iTotX, *piY, NULL); LineTo(hdc, prc->left + iTotX, *piY + iLineHeight*30/40); MoveToEx(hdc, prc->left, *piY, NULL); LineTo(hdc, prc->left + iTotX, *piY); MoveToEx(hdc, prc->left, *piY + iLineHeight*30/40, NULL); LineTo(hdc, prc->left + iTotX, *piY + iLineHeight*30/40); if (g_fShowLevels) { // Display bidi levels for each codepoint iItem = 0; iFont = 3; SelectObject(hdc, hf[3]); for (icp=0; icpleft + idx[icp-1], *piY + iLineHeight*35/40 + iSliderHeight, NULL); LineTo( hdc, prc->left + idx[icp-1], *piY + iLineHeight*35/40 + iSliderHeight + iLevelsHeight*35/40); } // Establish where horizontally to display the digit char chDigit = char('0' + items[iItem].a.s.uBidiLevel); int digitWidth; GetCharWidth32A(hdc, chDigit, chDigit, &digitWidth); ExtTextOutA( hdc, prc->left + (icp==0 ? idx[0]/2 : (idx[icp-1] + idx[icp])/2) - digitWidth / 2, *piY + iLineHeight*35/40 + iSliderHeight, 0, NULL, &chDigit, 1, NULL); } } // Check whether mouse clicks in this line are waiting to be processed if ( g_fOverrideDx && g_fMouseUp && g_iMouseUpY > *piY + iLineHeight*33/40 && g_iMouseUpY < *piY + iLineHeight*63/40) { // Procss change to DX override slider icp = 0; while (icpleft + idx[icp] < g_iMouseUpX) { icp++; } g_iWidthBuf[icpLineStart+icp] = *piY + 60 - g_iMouseUpY; // Adjust this slider InvalidateText(); // Force slider to redraw at new position g_fMouseDown = FALSE; g_fMouseUp = FALSE; g_iFrom = icpLineStart+icp; g_iTo = icpLineStart+icp; } else if (g_fMouseDown && g_iMouseDownY > *piY && g_iMouseDownY < *piY+iLineHeight) { // Handle text selection // Record char pos at left button down // Snap mouse hit to closest character boundary if (g_iMouseDownX < prc->left + idx[0]/2) { icp = 0; } else { icp = 1; while ( icp < iLen && g_iMouseDownX > prc->left + (idx[icp-1] + idx[icp]) / 2) { icp++; } } g_iFrom = icp + icpLineStart; if (g_iFrom < icpLineStart) { g_iFrom = icpLineStart; } if (g_iFrom > icpLineEnd) { g_iFrom = icpLineEnd; } g_fMouseDown = FALSE; } if (g_fMouseUp && g_iMouseUpY > *piY && g_iMouseUpY < *piY+iLineHeight) { // Complete selection processing if (g_iMouseUpX < prc->left + idx[0]/2) { icp = 0; } else { icp = 1; while ( icp < iLen && g_iMouseUpX > prc->left + (idx[icp-1] + idx[icp]) / 2) { icp++; } } g_iTo = icp + icpLineStart; if (g_iTo < icpLineStart) { g_iTo = icpLineStart; } if (g_iTo > icpLineEnd) { g_iTo = icpLineEnd; } // Caret is where mouse was raised g_iCurChar = g_iTo; g_iCaretSection = CARET_SECTION_LOGICAL; // Show caret in logical text g_fUpdateCaret = TRUE; g_fMouseUp = FALSE; // Signal that the mouse up is processed } if ( g_fUpdateCaret && g_iCurChar >= icpLineStart && g_iCurChar <= icpLineEnd && g_iCaretSection == CARET_SECTION_LOGICAL) { g_fUpdateCaret = FALSE; if (g_iCurChar <= icpLineStart) { ResetCaret(prc->left, *piY, iLineHeight); } else { ResetCaret(prc->left + idx[g_iCurChar - icpLineStart - 1], *piY, iLineHeight); } } } else { // ScriptGetCMap failed - therefore this is not a glyphable font. // This could indicate // A printer device font // We're running on FE Win95 which cannot handle glyph indices // // For the sample app, we know we are using a glyphable Truetype font // on a screen DC, so it must mean the sample is running on a Far // East version of Windows 95. // Theoretically we could go to the trouble of calling // WideCharToMultiByte and using the 'A' char interfaces to // implement DspLogcl. // However this is only a sample program - DspPlain and DspFormt // work correctly, but there's no advantage in implementing // DspLogcl so well. // Display an apology. ExtTextOutA(hdc, prc->left+2, *piY+2, ETO_CLIPPED, prc, "Sorry, no logical text display on Far East Windows 95.", 54, NULL); icpLineEnd = g_iTextLen; // Hack to stop display of subsequent lines } *piY += iLineHeight + iSliderHeight + iLevelsHeight; // Advance to next line if (g_fPresentation) { icpLineStart = g_iTextLen; // Only show one line in presentation mode } else { if (icpLineEnd < g_iTextLen) { icpLineEnd++; } if (icpLineEnd < g_iTextLen && g_wcBuf[icpLineEnd] == 0x0A) { icpLineEnd++; } icpLineStart = icpLineEnd; } } SelectObject(hdc, hfold); if (bHighlight) { // Turn off highlighting SetBkColor(hdc, dwOldBkColor); SetTextColor(hdc, dwOldTextColor); bHighlight = FALSE; } for (iFont=0; iFont