1134 lines
29 KiB
C++
1134 lines
29 KiB
C++
//// 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 && i<MAX_ITEM) {
|
|
bLevel[i++] = (BYTE) pRun->a.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; i<cRuns; i++) {
|
|
pRunVisual[cRuns-1-iLogical[i]] = pRun;
|
|
pRun = pRun->pNext;
|
|
}
|
|
} else {
|
|
for (i=0; i<cRuns; i++) {
|
|
pRunVisual[iLogical[i]] = pRun;
|
|
pRun = pRun->pNext;
|
|
}
|
|
}
|
|
|
|
|
|
// 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; i<cRuns; i++) {
|
|
|
|
pRun = pRunVisual[i];
|
|
iStyle = pRun->iStyle;
|
|
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; j<pRun->iLen-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);
|
|
}
|