[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