[user] tabbed text fixes.

Rein Klazes wijn at wanadoo.nl
Fri Oct 21 03:16:13 CDT 2005


Hi,

This is among others a fix for bug #3580. Because the code in
TabbedTextOut did not look very well, I have added some basic test cases
and made them pass on Wine ( some 60% failed with the original code)

Changelog:
dlls/user		: text.c
dlls/user/tests	: text.c

Fixed a lot of bugs in TEXT_TabbedTextOut. With the test cases used to
find them.

Rein. 
-------------- next part --------------
--- wine/dlls/user/text.c	2005-09-25 20:23:40.000000000 +0200
+++ mywine/dlls/user/text.c	2005-10-21 09:53:36.000000000 +0200
@@ -28,6 +28,7 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 
@@ -1214,17 +1215,13 @@ static LONG TEXT_TabbedTextOut( HDC hdc,
 {
     INT defWidth;
     SIZE extent;
-    int i;
+    int i, j;
     int start = x;
-    BOOL first = TRUE;
-
-    extent.cx = 0;
-    extent.cy = 0;
 
     if (!lpTabPos)
         cTabStops=0;
 
-    if (cTabStops == 1 && *lpTabPos >= /* sic */ 0)
+    if (cTabStops == 1)
     {
         defWidth = *lpTabPos;
         cTabStops = 0;
@@ -1234,66 +1231,70 @@ static LONG TEXT_TabbedTextOut( HDC hdc,
         TEXTMETRICA tm;
         GetTextMetricsA( hdc, &tm );
         defWidth = 8 * tm.tmAveCharWidth;
-        if (cTabStops == 1)
-            cTabStops = 0; /* on negative *lpTabPos */
     }
 
     while (count > 0)
     {
-        /* tokenize string by tabs */
+        RECT r;
+        INT x0;
+        x0 = x;
+        r.left = x0;
+        /* chop the string into substrings of 0 or more <tabs> 
+         * possibly followed by 1 or more normal characters */
         for (i = 0; i < count; i++)
-            if (lpstr[i] == '\t') break;
-
-        GetTextExtentPointW( hdc, lpstr, i, &extent );
-
-        /* the first time round the loop we should use the value of x
-         * passed into the function.
-         * all other times, we calculate it here */
-        if (!first)
-        {
+            if (lpstr[i] != '\t') break;
+        for (j = i; j < count; j++)
+            if (lpstr[j] == '\t') break;
+        /* get the extent of the normal character part */
+        GetTextExtentPointW( hdc, lpstr + i, j - i , &extent );
+        /* and if there is a <tab>, calculate its position */
+        if( i) {
             /* get x coordinate for the drawing of this string */
-            for (; cTabStops > 0; lpTabPos++, cTabStops--)
+            for (; cTabStops > i; lpTabPos++, cTabStops--)
             {
-                if (*lpTabPos >= 0)
-                {
-                    if (nTabOrg + *lpTabPos >= x)
-                    {
-                        x = nTabOrg + *lpTabPos;
+                if( nTabOrg + abs( *lpTabPos) > x) {
+                    if( lpTabPos[ i - 1] >= 0) {
+                        /* a left aligned tab */
+                        x = nTabOrg + lpTabPos[ i-1] + extent.cx;
                         break;
                     }
-                }
-                else
-                {
-                    /* if tab pos is negative then text is right-aligned to tab
-                     * stop meaning that the string extends to the left, so we
-                     * must subtract the width of the string */
-                    if (nTabOrg + -*lpTabPos -extent.cx >= x)
+                    else
                     {
-                        x = nTabOrg + -*lpTabPos - extent.cx;
-                        break;
+                        /* if tab pos is negative then text is right-aligned
+                         * to tab stop meaning that the string extends to the
+                         * left, so we must subtract the width of the string */
+                        if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x)
+                        {
+                            x = nTabOrg - lpTabPos[ i - 1];
+                            x0 = x - extent.cx;
+                            break;
+                        }
                     }
                 }
             }
             /* if we have run out of tab stops and we have a valid default tab
              * stop width then round x up to that width */
-            if ((cTabStops <= 0) && (defWidth > 0))
-                x = nTabOrg + ((x - nTabOrg) / defWidth + 1) * defWidth;
-        }
-        else first = FALSE;
-
+            if ((cTabStops <= i) && (defWidth > 0)) {
+                x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth;
+                x = x0 + extent.cx;
+            } else if ((cTabStops <= i) && (defWidth < 0)) {
+                x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i)
+                    * -defWidth;
+                x0 = x - extent.cx;
+            }
+        } else
+            x += extent.cx;
+        
         if (fDisplayText)
         {
-            RECT r;
-            r.left   = x;
             r.top    = y;
-            r.right  = x + extent.cx;
+            r.right  = x;
             r.bottom = y + extent.cy;
-            ExtTextOutW( hdc, x, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
-                         &r, lpstr, i, NULL );
+            ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
+                         &r, lpstr + i, j - i, NULL );
         }
-        x += extent.cx;
-        count -= i+1;
-        lpstr += i+1;
+        count -= j;
+        lpstr += j;
     }
     return MAKELONG(x - start, extent.cy);
 }
--- wine/dlls/user/tests/text.c	2005-04-01 13:32:17.000000000 +0200
+++ mywine/dlls/user/tests/text.c	2005-10-21 09:59:39.000000000 +0200
@@ -111,7 +111,97 @@ static void test_DrawTextCalcRect(void)
     ok( ret, "DestroyWindow error %lu\n", GetLastError());
 }
 
+/* replace tabs by \t */
+static void strfmt( char *str, char *strout)
+{
+    unsigned int i,j ;
+    for(i=0,j=0;i<=strlen(str);i++,j++)
+        if((strout[j]=str[i])=='\t') {
+            strout[j++]='\\';
+            strout[j]='t';
+        }
+}
+  
+
+#define TABTEST( tabval, tabcount, string, _exp) \
+{ int i,x_act, x_exp; char strdisp[64];\
+    for(i=0;i<8;i++) tabs[i]=(i+1)*(tabval); \
+    extent = GetTabbedTextExtentA( hdc, string, strlen( string), (tabcount), tabs); \
+    strfmt( string, strdisp); \
+ /*   trace( "Extent is %08lx\n", extent); */\
+    x_act = LOWORD( extent); \
+    x_exp = (_exp); \
+    ok( x_act == x_exp, "Test case \"%s\". Text extent is %d, expected %d tab %d tabcount %d\n", \
+        strdisp, x_act, x_exp, tabval, tabcount); \
+} \
+
+
+static void test_TabbedText()
+{
+    HWND hwnd;
+    HDC hdc;
+    BOOL ret;
+    TEXTMETRICA tm;
+    DWORD extent;
+    INT tabs[8], cx, cy, tab, tabcount,t,align;
+
+    /* Initialization */
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                           0, 0, 200, 200, 0, 0, 0, NULL);
+    ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
+    hdc = GetDC(hwnd);
+    ok(hdc != 0, "GetDC error %lu\n", GetLastError());
+
+    ret = GetTextMetricsA( hdc, &tm);
+    ok( ret, "GetTextMetrics error %lu\n", GetLastError());
+
+    extent = GetTabbedTextExtentA( hdc, "x", 1, 1, tabs);
+    cx = LOWORD( extent);
+    cy = HIWORD( extent);
+    trace( "cx is %d cy is %d\n", cx, cy);
+
+    align=1;
+    for( t=-1; t<=1; t++) { /* slightly adjust the 4 char tabstop, to 
+                               catch the one off errors */
+        tab =  (cx *4 + t);
+        /* test the special case tabcount =1 and the general array (80 of tabs */
+        for( tabcount = 1; tabcount <= 8; tabcount +=7) { 
+            TABTEST( align * tab, tabcount, "\t", tab)
+            TABTEST( align * tab, tabcount, "xxx\t", tab)
+            TABTEST( align * tab, tabcount, "\tx", tab+cx)
+            TABTEST( align * tab, tabcount, "\t\t", tab*2)
+            TABTEST( align * tab, tabcount, "\tx\t", tab*2)
+            TABTEST( align * tab, tabcount, "x\tx", tab+cx)
+            TABTEST( align * tab, tabcount, "xx\tx", tab+cx)
+            TABTEST( align * tab, tabcount, "xxx\tx", tab+cx)
+            TABTEST( align * tab, tabcount, "xxxx\tx", t>0 ? tab + cx : 2*tab+cx)
+            TABTEST( align * tab, tabcount, "xxxxx\tx", 2*tab+cx)
+        }
+    }
+    align=-1;
+    for( t=-1; t<=1; t++) { /* slightly adjust the 4 char tabstop, to 
+                               catch the one off errors */
+        tab =  (cx *4 + t);
+        /* test the special case tabcount =1 and the general array (8) of tabs */
+        for( tabcount = 1; tabcount <= 8; tabcount +=7) { 
+            TABTEST( align * tab, tabcount, "\t", tab)
+            TABTEST( align * tab, tabcount, "xxx\t", tab)
+            TABTEST( align * tab, tabcount, "\tx", tab)
+            TABTEST( align * tab, tabcount, "\t\t", tab*2)
+            TABTEST( align * tab, tabcount, "\tx\t", tab*2)
+            TABTEST( align * tab, tabcount, "x\tx", tab)
+            TABTEST( align * tab, tabcount, "xx\tx", tab)
+            TABTEST( align * tab, tabcount, "xxx\tx", 4 * cx >= tab ? 2*tab :tab)
+            TABTEST( align * tab, tabcount, "xxxx\tx", 2*tab)
+            TABTEST( align * tab, tabcount, "xxxxx\tx", 2*tab)
+        }
+    }
+
+
+}
+
 START_TEST(text)
 {
+    test_TabbedText();
     test_DrawTextCalcRect();
 }


More information about the wine-patches mailing list