//// TEXTDISP.C - Text display // // // // Copyright(c) 1997 - 1999. Microsoft Corporation. // #include "precomp.hxx" #include "uspglob.hxx" #pragma warning(disable:4702) // Unreachable code int iFirstLine; // Position at start of first line displayed void SelectStyle(HDC hdc, int iStyle, int *piLineHeight, int *piTop, HFONT *phOldFont) { HFONT hOldFont; HFONT hFont; TEXTMETRICA tma; hOldFont = NULL; if (ss[iStyle].fInUse) { hFont = CreateFontIndirectA(&ss[iStyle].rs.lf); if (hFont) { hOldFont = (HFONT) SelectObject(hdc, hFont); if (!*phOldFont) { *phOldFont = hOldFont; } else { DeleteObject(hOldFont); } } } GetTextMetricsA(hdc, &tma); if (!*piLineHeight) { *piLineHeight = ((tma.tmAscent + tma.tmDescent) * 133) / 100; } *piTop = ((*piLineHeight * 60) / 100) - tma.tmAscent; } BOOL PaintText(HDC hdc, int irs, int iY, PRECT prc, PWCH pwc, int iLen, PINT piLineHeight) { const BOOL fValidate = TRUE; BOOL fResult; HFONT hOldFont = NULL; int iTop; int iX; #ifdef LPK_TEST HRESULT hr; SCRIPT_STRING_ANALYSIS ssa; int iBuf[200]; POINT point; int i; SCRIPT_CONTROL ScriptControl; SCRIPT_STATE ScriptState; int iClipWidth; BOOL fValid; #endif SelectStyle(hdc, irs, piLineHeight, &iTop, &hOldFont); SetTextColor(hdc, g_iTextColor); if (fRight) { iX = prc->right - 1; SetTextAlign(hdc, TA_RIGHT); } else { iX = prc->left; SetTextAlign(hdc, TA_LEFT); } #ifdef LPK_TEST if (bUseLpk) { // Use LPK entrypoints memset(&ScriptControl, 0, sizeof(ScriptControl)); memset(&ScriptState, 0, sizeof(ScriptState)); ScriptControl.uDefaultLanguage = PrimaryLang; ScriptControl.fContextDigits = ContextDigits; ScriptState.fDigitSubstitute = (WORD) DigitSubstitute; ScriptState.fArabicNumContext = (WORD) ArabicNumContext; ScriptState.fDisplayZWG = (WORD) fDisplayZWG; iClipWidth = prc->right - prc->left - *piLineHeight; if (fFit || fClip) { if (fRight) { MoveToEx(hdc, iX - iClipWidth, 0, NULL); LineTo(hdc, iX - iClipWidth, prc->bottom); } else { MoveToEx(hdc, iX + iClipWidth, 0, NULL); LineTo(hdc, iX + iClipWidth, prc->bottom); } } hr = ScriptStringAnalyse( hdc, pwc, iLen, 0, -1, SSA_GLYPHS | SSA_LINK | (fRTL ? SSA_RTL : 0) | (fClip ? SSA_CLIP : 0) | (fFit ? SSA_FIT : 0) | (fFallback ? SSA_FALLBACK : 0) | (fTab ? SSA_TAB : 0) | (fHotkey ? SSA_HOTKEY : 0) | (fPassword ? SSA_PASSWORD : 0) | (fValidate ? SSA_BREAK : 0), iClipWidth, fNullState ? NULL : &ScriptControl, fNullState ? NULL : &ScriptState, NULL, NULL, NULL, &ssa); if (SUCCEEDED(hr)) { if (fPiDx) { // Hack a test of piDx. Generate a second analysis using the // Logical widths gleaned from the first analysis, but with // the first logical character doubled in width. ScriptStringGetLogicalWidths(ssa, iBuf); ScriptStringFree(&ssa); iBuf[0] *= 2; hr = ScriptStringAnalyse( hdc, pwc, iLen, 0, -1, SSA_GLYPHS | SSA_LINK | (fRTL ? SSA_RTL : 0) | (fClip ? SSA_CLIP : 0) | (fFit ? SSA_FIT : 0) | (fFallback ? SSA_FALLBACK : 0) | (fTab ? SSA_TAB : 0) | (fHotkey ? SSA_HOTKEY : 0) | (fPassword ? SSA_PASSWORD : 0) | (fValidate ? SSA_BREAK : 0), iClipWidth, fNullState ? NULL : &ScriptControl, fNullState ? NULL : &ScriptState, iBuf, NULL, NULL, &ssa); } } if (SUCCEEDED(hr)) { ScriptStringGetLogicalWidths(ssa, iBuf); if (fVertical) { hr = ScriptStringOut(ssa, *piLineHeight+iY+iTop, iX, 0, prc, 0, 0, FALSE); } else { hr = ScriptStringOut(ssa, iX, iY+iTop, 0, prc, 0, 0, FALSE); } fValid = ScriptStringValidate(ssa); switch (fValid) { case S_OK: ExtTextOutA(hdc, 0, iY, 0, NULL, "+", 1, NULL); break; case S_FALSE: ExtTextOutA(hdc, 0, iY, 0, NULL, "x", 1, NULL); break; default: ASSERTHR(fValid, ("PaintText -- ScriptStringValidate")); } if (iLen > sizeof(iBuf) / sizeof(iBuf[0])) { iLen = sizeof(iBuf) / sizeof(iBuf[0]); } ScriptStringGetLogicalWidths(ssa, iBuf); if (fRight) { iX = prc->right - ScriptString_pSize(ssa)->cx; } else { iX = prc->left; } MoveToEx(hdc, iX, iY+iTop, &point); LineTo(hdc, iX+ScriptString_pSize(ssa)->cx, iY+iTop); MoveToEx(hdc, iX, iY+iTop, &point); LineTo(hdc, iX, iY+iTop+10); for (i=0; i<*ScriptString_pcOutChars(ssa); i++) { iX += iBuf[i]; MoveToEx(hdc, iX, iY+iTop, &point); LineTo(hdc, iX, iY+iTop+10); } ScriptStringFree(&ssa); } fResult = SUCCEEDED(hr); } else { #endif // Call GDI directly if (fRTL) { SetTextAlign(hdc, GetTextAlign(hdc) | TA_RTLREADING); } if (fVertical) { fResult = ExtTextOutW(hdc, *piLineHeight + iY+iTop, iX, 0, prc, pwc, iLen, NULL); } else { fResult = ExtTextOutW(hdc, iX, iY+iTop, 0, prc, pwc, iLen, NULL); } #ifdef LPK_TEST } #endif if (fRight) { SetTextAlign(hdc, TA_LEFT); } if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return fResult; } //// ParseRun - Parse forward to next markup / end of line / end of text // // Returns P_MARKUP or P_CRLF or P_EOT in *piType. BOOL ParseRun(int iPos, PINT piLen, PINT piType, PINT piMLen, PINT piMVal) { int iT; // Parse type int iL; // Parse length int iV; // Parse value int i; i = iPos; if (!textParseForward(i, &iL, &iT, &iV)) { return FALSE; } while (iT != P_CRLF && iT != P_EOT && iT != P_MARKUP) { i += iL; if (!textParseForward(i, &iL, &iT, &iV)) { return FALSE; } } *piLen = i - iPos; *piType = iT; *piMLen = iL; *piMVal = iV; return TRUE; } //// PaintLineLogical // // Simply paints each run separately, to the right of the previous run. BOOL PaintLineLogical(HDC hdc, int iPos, int iY, PRECT prc, PINT piLineHeight) { int iType; // Item type int iML; // Markup length int iMV; // Markup value int iLen; // Length to markup int irs; // Run style irs = 0; while (ParseRun(iPos, &iLen, &iType, &iML, &iMV)) { // Process any text up to the markup point if (iLen) { if (!PaintText(hdc, 0, iY, prc, textpChar(iPos), iLen, piLineHeight)) { return FALSE; } iPos += iLen; } // Now process markup switch(iType) { case P_MARKUP: irs = iMV; break; case P_CRLF: case P_EOT: return TRUE; } iPos += iML; } return FALSE; } struct RUN { struct RUN *pNext; int iCharPos; int iLen; SCRIPT_ANALYSIS a; int iStyle; // Index to style sheet (global 'ss'). }; //// ParseLine - Parse forward to next end of line / end of text // // Returns a linked list of RUNs. BOOL ParseLine(int iPos, int *piLen, int *piT, struct RUN **ppRun) { int iL; // Parse length int iV; // Parse value int i; struct RUN **ppLastRun = ppRun; struct RUN *pRun = NULL; int iRunStart; int iRunStyle; i = iPos; iRunStart = iPos; iRunStyle = 0; // Initial style is default style while (textParseForward(i, &iL, piT, &iV)) { if (*piT != P_CHAR) { // Record current run if (ppRun && i > iRunStart) { pRun = new RUN; pRun->iCharPos = iRunStart - iPos; pRun->iLen = i - iRunStart; pRun->iStyle = iRunStyle; pRun->pNext = NULL; (*ppLastRun) = pRun; ppLastRun = &pRun->pNext; } switch (*piT) { case P_MARKUP: iRunStyle = iV; iRunStart = i+iL; break; case P_CRLF: case P_EOT: *piLen = i-iPos; return TRUE; } } i += iL; } // If we dropped out of the loop, we failed. return FALSE; } //// DrawArrow // // Draws an arrow under a single run indicating it's extent and direction. void DrawArrow( HDC hdc, int x, int y, ABC abc, BOOL fRTL, int iLevel, BOOL bFallback, BOOL bScriptUndef) { POINT p; HFONT hFont; HFONT hOldFont; char sLevel[10]; int iLen; SIZE size; int iOldBkMode; HPEN hPen; HPEN hOldPen; hFont = CreateFontA(-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, "Small Fonts"); hOldFont = (HFONT) SelectObject(hdc, hFont); if (fRTL) { iLen = wsprintfA(sLevel, "< %d", iLevel); } else { iLen = wsprintfA(sLevel, "%d >", iLevel); } GetTextExtentPoint32A(hdc, sLevel, iLen, &size); iOldBkMode = SetBkMode(hdc, TRANSPARENT); hPen = CreatePen(PS_SOLID, 0, bScriptUndef ? RGB(255,0,0) : (bFallback ? RGB(0,0,255) : RGB(0,0,0))); hOldPen = HPEN(SelectObject(hdc, hPen)); MoveToEx(hdc, x, y+12, &p); LineTo (hdc, x+abc.abcA, y); LineTo (hdc, x+abc.abcA+abc.abcB, y); LineTo (hdc, x+abc.abcA+abc.abcB+abc.abcC, y+12); ExtTextOutA(hdc, x+abc.abcA+(abc.abcB-size.cx)/2, y, 0, NULL, sLevel, iLen, NULL); SelectObject(hdc, hOldPen); DeleteObject(hPen); SelectObject(hdc, hOldFont); DeleteObject(hFont); SetBkMode(hdc, iOldBkMode); } //// PaintLineVisual // // Parses each run separately and passes them to visual.c to display // // Creates a buffer with one byte per character in the input line containing // the style index for that character. // // Markup characters are given style -1 to indicate that they're not part // of the line. // // The line and style buffers are passed out to visual.c for display and cursor // position analysis. // // PaintLineVisual processes any pending mouse click in a displayed run // and updates global iCharpos accordingly. // // PaintLineVisual also sets the GDI caret position if it displays a // run containing iCharPos. // // *pfCharPosDirty is usually returned FALSE. When the user clicks in // a run, the sequence of processing in PaintLineVisual will usually // display the cursor subsequent to detecting the mouse click position. // However, when the user clicks in the leading half of the first // character of a run, the caret is displayed on the trailing edge of // the logically preceeding character. If that character has already // been displayed in a prior run, PaintLineVisual will exit with // *pdCharPosDirty TRUE - in this case it must be called a second time // to display the correct cursor position. BOOL PaintLineVisual(HDC hdc, int iPos, int iY, PRECT prc, PINT piLineHeight, BOOL *pfCharPosDirty) { int iType; // Item type int iStyle; // Run style int iOriginalStyle;// Fallback support int iStylePos; int iTextPos; int iLineLen; int iRealLen; // Actual characters (excluding markup, CRLF etc). int i,j; int iX; WCHAR *pRealString; // Actual string (excluding markup) struct RUN *pFirstRun; struct RUN *pRun; struct RUN *pNewRun; WCHAR *pwc; SCRIPT_CONTROL ScriptControl; SCRIPT_STATE ScriptState; #define MAX_ITEM 100 SCRIPT_ITEM Items[MAX_ITEM]; int cItems; int iItem; int iItemLen; BYTE bLevel[MAX_ITEM]; int iLogical[MAX_ITEM]; struct RUN *pRunVisual[MAX_ITEM]; int cRuns; #define MAX_GLYPHS 200 WORD glyphs[MAX_GLYPHS]; WORD wClusters[MAX_ITEM]; SCRIPT_VISATTR visattrs[MAX_GLYPHS]; int iAdvance[MAX_GLYPHS]; GOFFSET Goffset[MAX_GLYPHS]; int cGlyphs; ABC abc; HRESULT hr; int iCaretX; HFONT hOldFont; int iTop; iTextPos = iPos; iStyle = 0; *pfCharPosDirty = FALSE; if (fRight) { iX = prc->right; } else { iX = prc->left; } // Establish line length in characters and allocate buffers ParseLine(iPos, &iLineLen, &iType, &pFirstRun); if (iLineLen == 0) { // Empty line! return TRUE; } // Establish real characters in the line iRealLen = 0; pRun = pFirstRun; while (pRun) { iRealLen += pRun->iLen; pRun = pRun->pNext; } // Copy the real characters to a private buffer pRealString = (WCHAR *) malloc(sizeof(WCHAR) * iRealLen); pRun = pFirstRun; pwc = pRealString; while (pRun) { memcpy((void *)pwc, wcBuf+iPos+pRun->iCharPos, pRun->iLen * sizeof(WCHAR)); pwc += pRun->iLen; pRun = pRun->pNext; } // Itemize by script memset(&ScriptControl, 0, sizeof(ScriptControl)); memset(&ScriptState, 0, sizeof(ScriptState)); ScriptControl.uDefaultLanguage = PrimaryLang; ScriptControl.fContextDigits = ContextDigits; ScriptState.fDigitSubstitute = (WORD) DigitSubstitute; ScriptState.fArabicNumContext = (WORD) ArabicNumContext; ScriptState.fDisplayZWG = (WORD) fDisplayZWG; if (fRTL) { ScriptState.uBidiLevel = 1; // Start at an odd level for RTL } hr = ScriptItemize( pRealString, iRealLen, MAX_ITEM, fNullState ? NULL : &ScriptControl, fNullState ? NULL : &ScriptState, Items, &cItems); ASSERTHR(hr, ("ScriptItemize failed")); if (FAILED(hr)) { free(pRealString); return FALSE; } // Merge script items into runs pRun = pFirstRun; iItem = 0; iItemLen = Items[iItem+1].iCharPos - Items[iItem].iCharPos; while (iItem < cItems && pRun) { pRun->a = Items[iItem].a; // Record script analysis if (iItemLen < pRun->iLen) { // This item is shorter than this run: split this run in two pNewRun = new RUN; pNewRun->pNext = pRun->pNext; pNewRun->iCharPos = pRun->iCharPos + iItemLen; pNewRun->iLen = pRun->iLen - iItemLen; pNewRun->iStyle = pRun->iStyle; pRun->iLen = iItemLen; pRun->pNext = pNewRun; iItem++; iItemLen = Items[iItem+1].iCharPos - Items[iItem].iCharPos; } else if (iItemLen > pRun->iLen) { // This item is longer than this run: advance run leaving remainder for next run iItemLen -= pRun->iLen; } else { // Run matches item iItem++; iItemLen = Items[iItem+1].iCharPos - Items[iItem].iCharPos; } pRun = pRun->pNext; } // Each run is now a single script, and includes the SCRIPT_ANALYSIS flags. // Build layout array i = 0; pRun = pFirstRun; while (pRun && ia.s.uBidiLevel; pRun = pRun->pNext; } cRuns = i; hr = ScriptLayout(cRuns, bLevel, NULL, iLogical); ASSERTHR(hr, ("ScriptLayout failed")); if (FAILED(hr)) { free(pRealString); return FALSE; } pRun = pFirstRun; if (fRight) { // Create right aligned in reverse order for (i=0; ipNext; } } else { for (i=0; ipNext; } } // Process runs one at a time, in visual order for left aligned, // reverse visual order for right aligned. hOldFont = NULL; SetBkMode(hdc, TRANSPARENT); for (i=0; iiStyle; iOriginalStyle = iStyle; SelectStyle(hdc, iStyle, piLineHeight, &iTop, &hOldFont); pRun->a.fLogicalOrder = (WORD) fLogicalOrder; hr = ScriptShape( hdc, &ss[iStyle].rs.sc, wcBuf+iPos+pRun->iCharPos, pRun->iLen, MAX_GLYPHS, &pRun->a, glyphs, wClusters, visattrs, &cGlyphs); if (hr == USP_E_SCRIPT_NOT_IN_FONT) { // Font association - loop round all available styles iStyle = 0; while (hr == USP_E_SCRIPT_NOT_IN_FONT && iStyle < MAX_STYLES) { if (iStyle != iOriginalStyle) { SelectStyle(hdc, iStyle, piLineHeight, &iTop, &hOldFont); hr = ScriptShape( hdc, &ss[iStyle].rs.sc, wcBuf+iPos+pRun->iCharPos, pRun->iLen, MAX_GLYPHS, &pRun->a, glyphs, wClusters, visattrs, &cGlyphs); } iStyle++; } iStyle--; } if (FAILED(hr)) { if (hr == USP_E_SCRIPT_NOT_IN_FONT) { TRACEMSG(("No font supports run %d", i)); } else { ASSERTHR(hr, ("PaintLineVisual -- ScriptShape run %d style %d", i, iOriginalStyle)); } // Use Style 0 with SCRIPT_UNDEFINED iStyle = 0; SelectStyle(hdc, iStyle, piLineHeight, &iTop, &hOldFont); pRun->a.eScript = SCRIPT_UNDEFINED; hr = ScriptShape( hdc, &ss[iStyle].rs.sc, wcBuf+iPos+pRun->iCharPos, pRun->iLen, MAX_GLYPHS, &pRun->a, glyphs, wClusters, visattrs, &cGlyphs); ASSERTHR(hr, ("Run %d failed even with SCRIPT_UNDEFINED", i)); if (FAILED(hr)) { free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return FALSE; } } //// For debugging, treat e or period followed by any char as a cluster for (j=0; jiLen-1; j++) { if ( wcBuf[iPos+pRun->iCharPos+j] == 'e' || wcBuf[iPos+pRun->iCharPos+j] == '.') { visattrs[wClusters[j+1]].fClusterStart = FALSE; wClusters[j+1] = wClusters[j]; } } hr = ScriptPlace( hdc, &ss[iStyle].rs.sc, glyphs, cGlyphs, visattrs, &pRun->a, iAdvance, Goffset, &abc); ASSERTHR(hr, ("ScriptPlace failed")); if (FAILED(hr)) { free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return FALSE; } if (fRight) { iX -= abc.abcA + abc.abcB + abc.abcC; } hr = ScriptTextOut( hdc, &ss[iStyle].rs.sc, fVertical ? *piLineHeight + iY+iTop : iX, fVertical ? iX : iY+iTop, NULL, NULL, &pRun->a, wcBuf+iPos+pRun->iCharPos, // original string (required only for metafiles) pRun->iLen, glyphs, cGlyphs, iAdvance, NULL, // No justification Goffset); ASSERTHR(hr, ("ScriptTextOut: ")); if (FAILED(hr)) { free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return FALSE; } // Test for ScriptXtoCP limit handling BOOL fTrailing; int iCP; hr = ScriptXtoCP( 10000, pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCP, &fTrailing); // Test for ScriptCPtoX limit handling int iTestX; hr = ScriptCPtoX( 10000, FALSE, pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iTestX); // Process any pending mouse click in this run if ( Click.fNew && Click.yPos >= iY && Click.yPos < iY+*piLineHeight && Click.xPos > iX && Click.xPos <= iX+(int)(abc.abcA + abc.abcB + abc.abcC)) { // Mouse click in this run Click.fNew = FALSE; int iCP; BOOL fTrailing; if (pRun->a.fLogicalOrder && pRun->a.fRTL) { // RTL logical order offsets are from right end hr = ScriptXtoCP( abc.abcA + abc.abcB + abc.abcC - (Click.xPos - iX), pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCP, &fTrailing); } else { hr = ScriptXtoCP( Click.xPos - iX, pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCP, &fTrailing); } ASSERTHR(hr, ("ScriptXtoCP failed")); if (FAILED(hr)) { free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return FALSE; } iCurChar = iPos + pRun->iCharPos + iCP + fTrailing; *pfCharPosDirty = TRUE; } // Process caret display in this run if ( iCurChar > iPos + pRun->iCharPos && iCurChar <= iPos + pRun->iCharPos + pRun->iLen) { // Caret is somewhere within this run if (gCaretToStart) { hr = ScriptCPtoX( -1, TRUE, // Trailing edge of virtual character before run pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCaretX); gCaretToStart = FALSE; } else if (gCaretToEnd) { hr = ScriptCPtoX( pRun->iLen, FALSE, // Leading edge of virtual character after run pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCaretX); gCaretToEnd = FALSE; } else { hr = ScriptCPtoX( iCurChar - (iPos+pRun->iCharPos) - 1, TRUE, // Yes, want trailing edge of character pRun->iLen, cGlyphs, wClusters, visattrs, iAdvance, &pRun->a, &iCaretX); } ASSERTHR(hr, ("ScriptCPtoX failed")); if (FAILED(hr)) { free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } return FALSE; } if (pRun->a.fLogicalOrder && pRun->a.fRTL) { // RTL logical order offsets are from right end SetCaretPos(iX + abc.abcA + abc.abcB + abc.abcC - iCaretX, iY); } else { SetCaretPos(iX+iCaretX, iY); } *pfCharPosDirty = FALSE; } // Superimpose run extent, direction and bidi level markings DrawArrow(hdc, iX, iY, abc, pRun->a.fRTL, pRun->a.s.uBidiLevel, iStyle != iOriginalStyle, pRun->a.eScript == SCRIPT_UNDEFINED); if (!fRight) { iX += abc.abcA + abc.abcB + abc.abcC; } } free(pRealString); if (hOldFont) { DeleteObject(SelectObject(hdc, hOldFont)); } // If the caret is at the start of the line, set it's position now. if (iCurChar == iPos) { if (fRTL) { if (fRight) { SetCaretPos(prc->right, iY); } else { SetCaretPos(iX, iY); } } else { if (fRight) { SetCaretPos(iX, iY); } else { // LTR, Left aligned SetCaretPos(prc->left, iY); } } *pfCharPosDirty = FALSE; } return TRUE; UNREFERENCED_PARAMETER(prc); UNREFERENCED_PARAMETER(iStylePos); } //// dispPaint - Display current text range // BOOL dispPaint(HDC hdc, PRECT prc) { int iPos; int iType; int iY; int iLen; int iLineHeight; BOOL fCharPosDirty; iPos = 0; iLineHeight = 0; iY = 0; while (ParseLine(iPos, &iLen, &iType, NULL)) { if (iLen) { if (!PaintText(hdc, 0, iY, prc, textpChar(iPos), iLen, &iLineHeight)) { TRACEMSG(("CSTEST.dispPaint: PaintText failed")); } } iY += iLineHeight; #ifdef LOGICAL_LINES if (iLen) { PaintLineLogical(hdc, iPos, iY, prc, &iLineHeight); } iY += iLineHeight; #endif if (iLen) { PaintLineVisual(hdc, iPos, iY, prc, &iLineHeight, &fCharPosDirty); if (fCharPosDirty) { // Redisplay to correct caret position PaintLineVisual(hdc, iPos, iY, prc, &iLineHeight, &fCharPosDirty); } } iY += iLineHeight * 5 / 4; if (iY > prc->bottom) { return TRUE; } switch (iType) { case P_EOT: return TRUE; case P_CRLF: iPos += iLen + 2; break; } } return FALSE; } //// dispInvalidate // // BOOL dispInvalidate(HWND hWnd, int iPos) { InvalidateRect(hWnd, NULL, TRUE); return TRUE; UNREFERENCED_PARAMETER(iPos); }