[LPK] Implement LpkPSMTextOut(). (#890)
[reactos.git] / dll / win32 / lpk / lpk.c
index 03ed326..b99e8a3 100644 (file)
  */
 
 #include "ros_lpk.h"
+#include <debug.h>
 
 LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate,       EditIchToXY,  EditMouseToIch, EditCchInWidth,
                                          EditGetLineWidth, EditDrawText, EditHScroll,    EditMoveSelection,
                                          EditVerifyText,   EditNextWord, EditSetMenu,    EditProcessMenu,
                                          EditCreateCaret, EditAdjustCaret};
 
+#define PREFIX 38
+#define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
+#define KANA_PREFIX  31 /* Win16: Katakana prefix */
+
+static int PSM_FindLastPrefix(LPCWSTR str, int count)
+{
+    int i, prefix_count = 0, index = -1;
+
+    for (i = 0; i < count - 1; i++)
+    {
+        if (str[i] == PREFIX && str[i + 1] != PREFIX)
+        {
+            index = i - prefix_count;
+            prefix_count++;
+        }
+        else if (str[i] == PREFIX && str[i + 1] == PREFIX)
+        {
+            i++;
+        }
+    }
+    return index;
+}
+
+static void PSM_PrepareToDraw(LPCWSTR str, INT count, LPWSTR new_str, LPINT new_count)
+{
+    int len, i = 0, j = 0;
+
+    while (i < count)
+    {
+        if (str[i] == PREFIX || (iswspace(str[i]) && str[i] != L' '))
+        {
+            if(i < count - 1 && str[i + 1] == PREFIX)
+                new_str[j++] = str[i++];
+            else
+                i++;
+        }
+        else
+        {
+            new_str[j++] = str[i++];
+        }
+    }
+
+    new_str[j] = L'\0';   
+    len = wcslen(new_str);
+    *new_count = len;
+}
+
+/* Can be used with also LpkDrawTextEx if it will be implemented */
+static void LPK_DrawUnderscore(HDC hdc, int x, int y, LPCWSTR str, int count, int offset)
+{
+    SCRIPT_STRING_ANALYSIS ssa;
+    int prefix_x;
+    int prefix_end;
+    int pos;
+    SIZE size;
+    HPEN hpen;
+    HPEN oldPen;
+    HRESULT hr = S_FALSE;
+
+    if (offset == -1)
+        return;
+
+    if (ScriptIsComplex(str, count, SIC_COMPLEX) == S_OK)
+    {
+        hr = ScriptStringAnalyse(hdc, str, count, (3 * count / 2 + 16),
+                                -1, SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &ssa);
+    }
+
+    if (hr == S_OK)
+    {
+        ScriptStringCPtoX(ssa, offset, FALSE, &pos);
+        prefix_x = x + pos;
+        ScriptStringCPtoX(ssa, offset, TRUE, &pos);
+        prefix_end = x + pos;
+        ScriptStringFree(&ssa);
+    }
+    else
+    {
+        GetTextExtentPointW(hdc, str, offset, &size);
+        prefix_x = x + size.cx;
+        GetTextExtentPointW(hdc, str, offset + 1, &size);
+        prefix_end = x + size.cx - 1;
+    }
+    hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
+    oldPen = SelectObject(hdc, hpen);
+    MoveToEx(hdc, prefix_x, y, NULL);
+    LineTo(hdc, prefix_end, y);
+    SelectObject(hdc, oldPen);
+    DeleteObject(hpen);
+}
+
 BOOL
 WINAPI
 DllMain(
@@ -66,28 +158,31 @@ LpkExtTextOut(
     LPWORD glyphs = NULL;
     LPWSTR reordered_str = NULL;
     INT cGlyphs;
-    BOOL bResult;
+    DWORD dwSICFlags = SIC_COMPLEX;
+    BOOL bResult, bReorder;
 
     UNREFERENCED_PARAMETER(unknown);
 
-    if (!(fuOptions & ETO_IGNORELANGUAGE))
-        fuOptions |= ETO_IGNORELANGUAGE;
+    fuOptions |= ETO_IGNORELANGUAGE;
 
     /* Check text direction */
     if ((GetLayout(hdc) & LAYOUT_RTL) || (GetTextAlign(hdc) & TA_RTLREADING))
-    {
-        if (!(fuOptions & ETO_RTLREADING))
-            fuOptions |= ETO_RTLREADING;
-    }
+        fuOptions |= ETO_RTLREADING;
+
+    /* If text direction is RTL change flag to account neutral characters */
+    if (fuOptions & ETO_RTLREADING)
+        dwSICFlags |= SIC_NEUTRAL;
 
     /* Check if the string requires complex script processing and not a "glyph indices" array */
-    if (ScriptIsComplex(lpString, uCount, SIC_COMPLEX) == S_OK && !(fuOptions & ETO_GLYPH_INDEX))
+    if (ScriptIsComplex(lpString, uCount, dwSICFlags) == S_OK && !(fuOptions & ETO_GLYPH_INDEX))
     {
+        /* reordered_str is used as fallback in case the glyphs array fails to generate,
+           BIDI_Reorder doesn't attempt to write into reordered_str if memory allocation fails */
         reordered_str = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WCHAR));
 
-        BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
-                     (fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
-                     reordered_str, uCount, NULL, &glyphs, &cGlyphs);
+        bReorder = BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
+                                (fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
+                                reordered_str, uCount, NULL, &glyphs, &cGlyphs);
 
         if (glyphs)
         {
@@ -95,14 +190,17 @@ LpkExtTextOut(
             uCount = cGlyphs;
         }
 
-        if (glyphs || reordered_str)
+        /* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder succeeded */
+        if ((glyphs || reordered_str) && bReorder) 
         {
             bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc,
                                   glyphs ? (LPWSTR)glyphs : reordered_str, uCount, lpDx);
         }
-
         else
+        {
+            DPRINT1("BIDI_Reorder failed, falling back to original string.\n");
             bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
+        }
 
         HeapFree(GetProcessHeap(), 0, glyphs);
         HeapFree(GetProcessHeap(), 0, reordered_str);
@@ -127,9 +225,11 @@ LpkGetCharacterPlacement(
     DWORD dwFlags,
     DWORD dwUnused)
 {
+    DWORD ret = 0;
+    HRESULT hr;
+    SCRIPT_STRING_ANALYSIS ssa;
     LPWORD lpGlyphs = NULL;
     SIZE size;
-    DWORD ret = 0;
     UINT nSet, i;
     INT cGlyphs;
 
@@ -152,18 +252,17 @@ LpkGetCharacterPlacement(
     {
         if (lpGlyphs)
             StringCchCopyW(lpResults->lpGlyphs, cGlyphs, lpGlyphs);
-
         else if (lpResults->lpOutString)
             GetGlyphIndicesW(hdc, lpResults->lpOutString, nSet, lpResults->lpGlyphs, 0);
     }
 
     if (lpResults->lpDx)
     {
+        int c;
+
         /* If glyph shaping was requested */
         if (dwFlags & GCP_GLYPHSHAPE)
         {
-            int c;
-
             if (lpResults->lpGlyphs)
             {
                 for (i = 0; i < lpResults->nGlyphs; i++)
@@ -176,8 +275,6 @@ LpkGetCharacterPlacement(
 
         else
         {
-            int c;
-
             for (i = 0; i < nSet; i++)
             {
                 if (GetCharWidth32W(hdc, lpResults->lpOutString[i], lpResults->lpOutString[i], &c))
@@ -186,16 +283,29 @@ LpkGetCharacterPlacement(
         }
     }
 
-    /* FIXME: Currently not bidi compliant! */
     if (lpResults->lpCaretPos)
     {
         int pos = 0;
 
-        lpResults->lpCaretPos[0] = 0;
-        for (i = 1; i < nSet; i++)
+        hr = ScriptStringAnalyse(hdc, lpString, nSet, (3 * nSet / 2 + 16), -1, SSA_GLYPHS, -1,
+                                 NULL, NULL, NULL, NULL, NULL, &ssa);
+        if (hr == S_OK)
         {
-            if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
-                lpResults->lpCaretPos[i] = (pos += size.cx);
+            for (i = 0; i < nSet; i++)
+            {
+                if (ScriptStringCPtoX(ssa, i, FALSE, &pos) == S_OK)
+                    lpResults->lpCaretPos[i] = pos;
+            }
+            ScriptStringFree(&ssa);
+        }
+        else
+        {
+            lpResults->lpCaretPos[0] = 0;
+            for (i = 1; i < nSet; i++)
+            {
+                if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
+                    lpResults->lpCaretPos[i] = (pos += size.cx);
+            }
         }
     }
 
@@ -206,3 +316,55 @@ LpkGetCharacterPlacement(
 
     return ret;
 }
+
+/* Stripped down version of DrawText, can only draw single line text and Prefix underscore
+ * (only on the last found amperstand)
+ * only flags to be found to be of use in testing:
+ * 
+ * DT_NOPREFIX   - Draw the string as is without removal of the amperstands and without underscore
+ * DT_HIDEPREFIX - Draw the string without underscore
+ * DT_PREFIXONLY - Draw only the underscore
+ * 
+ * without any of these flags the behavior is the string being drawn without the amperstands and
+ * with the underscore.
+ * user32 has an equivalent function - UserLpkPSMTextOut
+ */
+INT WINAPI LpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
+{
+    SIZE size;
+    TEXTMETRICW tm;
+    int prefix_offset, len;
+    LPWSTR display_str = NULL;
+
+    if (!lpString || cString <= 0)
+        return 0;
+
+    if (dwFlags & DT_NOPREFIX)
+    {
+        LpkExtTextOut(hdc, x, y, (dwFlags & DT_RTLREADING) ? ETO_RTLREADING : 0, NULL, lpString, cString - 1, NULL, 0);
+        GetTextExtentPointW(hdc, lpString, cString, &size);
+        return size.cx;
+    }
+
+    display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
+
+    if (!display_str)
+        return 0;
+
+    PSM_PrepareToDraw(lpString, cString, display_str, &len);
+
+    if (!(dwFlags & DT_PREFIXONLY))
+        LpkExtTextOut(hdc, x, y, (dwFlags & DT_RTLREADING) ? ETO_RTLREADING : 0, NULL, display_str, len, NULL, 0);
+
+    if (!(dwFlags & DT_HIDEPREFIX))
+    {
+        prefix_offset = PSM_FindLastPrefix(lpString, cString);
+        GetTextMetricsW(hdc, &tm);
+        LPK_DrawUnderscore(hdc, x, y + tm.tmAscent + 1, display_str, len, prefix_offset);
+    }
+
+    GetTextExtentPointW(hdc, display_str, len + 1, &size);
+    HeapFree(GetProcessHeap(), 0, display_str);
+
+    return size.cx;
+}