Alexandre Julliard e2991ea7bd Release 950727
Sat Jul 22 22:39:09 IDT 1995 Michael Veksler <e1678223@tochnapc2.technion.ac.il>

	* [ipc/*]
	New directory. This directory contains the new inter-wine
 	communications support. It enables DDE protocols between two wine
 	instances.  Currently it is limited to DDE, but can be enhanced to
 	support OLE between 2 different wine instances.  This is very
 	important for libwine.a DDE/OLE support.

	* [tools/ipcl]
    	A script to delete garbage IPC handles (shared memory, semaphores
 	and message queues).  The current inter-wine communication is not
 	perfect, and sometimes leaves garbage behind.

	* [if1632/relay.c] [include/atom.h] [include/global.h]
 	[loader/selector.c] [loader/task.c] [loader/module.c]
 	[loader/signal.c] [memory/global.c] [misc/atom.c]
 	[windows/class.c] [windows/message.c] [windows/win.c]
	[Imakefile]
    	Hooks for inter-wine DDE support, current Global.*Atom functions
 	renamed to Local.*Atom since Global.*Atom are used for Inter-Wine
 	DDE communication. (The first call to these functions sets up the
 	IPC structures - which otherwise cause unneeded overhead.

Mon Jul 17 19:55:21 1995  Alexandre Julliard  <julliard@sunsite.unc.edu>

	* [controls/menu.c]
	Don't crash if a NULL string is passed to menu functions.

	* [memory/selector.c]
	We now use a bit in ldt_flags_copy to indicate free LDT entries.
	Fixed a bug in SELECTOR_ReallocBlock that could cause it to
	overwrite valid LDT entries when growing a block.

	* [miscemu/instr.c]
	Emulate int xx instruction by storing the interrupt vector in
	CS:IP and returning directly. This allows a program to install an
	interrupt vector.

	* [windows/win.c]
	Added function WIN_GetTopParent to get the top-level parent of a
	window.

Sun Jul  16 18:17:17 1995  Gregory Trubetskoy <grisha@mira.com>

        * [loader/resource.c]
        Added LoadIconHandler. It doesn't do anything yet, but now you
        can use borland help files with winhelp.exe.

Sun Jul 16 11:58:45 1995 Anand Kumria <akumria@ozemail.com.au>

	* [misc/main.c]
	Fixed to return 386 Enhanced mode correctly. Also return the same
 	type of CPU, for both Enhanced and Standard mode, namely a 386.

Sun Jul 16 00:02:04 1995    Martin von Loewis <loewis@informatik.hu-berlin.de>

	* [Configure] [include/options.h] [include/wineopts.h]
	  [misc/main.c][misc/spy.c]
	  Removed support of spy file. Redirected spy messages to stddeb.
	  Removed -spy option. Added -debugmsg +spy option.

	* [debugger/dbg.y][debugger/debug.l]
	Enabled segmented addresses (seg:offs) for break and x commands.

	* [if1632/gdi.spec] [objects/region.c] [windows/graphics.c]
	  [include/region.h]
	FrameRgn, REGION_FrameRgn: New functions

	* [if1632/kernel.spec]
	IsWinOldApTask: Return false

	* [if1632/mouse.spec]
	CplApplet: Removed

	* [if1632/user.spec] [windows/win.c]
	ShowOwnedPopups: New function

	* [if1632/winsock.spec] [misc/winsocket.c]
	inet_addr, select: New prototypes in relay code
	Fixed memory layout for netdb functions (getXbyY).
	WINSOCK_ioctlsocket: Translated FIONREAD, FIONBIO, and FIOASYNC

	* [objects/clipping.c]
	RectVisible: Fixed call to LPToDP

	* [rc/winerc.c]
	main: Removed extra argument to getopt for Linux.

Tue Jul 11 00:14:41 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>

        * [controls/listbox.c]
	Yet another fix for ListBoxDirectory().
	
	* [loader/module.c] [if1632/kernel.spec]
	Make GetModuleHandle() accept instance handles as parameter.

        * [if1632/relay.c] [loader/task.c]
	Put a magic cookie at the bottom of the 32 bit stack, and check on
	each return from a 32 bit function whether it's still there. Complain
	if it's not.

        * [if1632/user.spec]
	Wrong entry for CloseDriver().

	* [misc/dos_fs.c] [loader/task.c] [include/dos_fs.h] [misc/file.c]
	[miscemu/int21.c]
	Large parts of dos_fs.c simplified. Changed it to use one
	current drive/directory per task, which is set to the module path on
	task creation.
	Prevent CorelPaint from closing stdin.
	open() with O_CREAT set must be passed three parameters.
	DOS FindFirst()/FindNext() could crash when FA_LABEL was set. Fixed,
	it's in DOS_readdir() now.

	* [misc/profile.c]
	Some badly written software (Lotus Freelance Graphics) passes a bogus
	size parameter that caused Wine to write off the end of a segment.
	Fixed. (It's probably too paranoid now.)
	
	* [multimedia/mmsystem.c] [multimedia/time.c] [multimedia/joystick.c]
	[multimedia/Imakefile] [if1632/winprocs.spec]
	16 bit entry point for MMSysTimeCallback.
	Split off time.c and joystick.c from mmsystem.c.
	
	* [objects/dib.c]
	GetDIBits(): call XGetImage() via CallTo32_LargeStack.

        * [windows/cursor.c]
	DestroyCursor(): do nothing for builtin cursors.
	
	* [windows/mdi.c]
	Half of WM_MDISETMENU implemented.
	
	* [windows/win.c]
	EnumWindows() and EnumTaskWindows() never enumerated any windows.
	Fixed.

	* [windows/*.c]
	Fixed GetParent() to return correct values for owned windows.

	* [windows/message.c]
	Don't try to activate disabled top-level windows.

        * [windows/nonclient.c]
	Work around a bug in gcc-2.7.0.
	
	* [tools/build.c] [include/stackframe.h] [memory/global.c] 
	[loader/task.c] [memory/selector.c]
	Some Visual Basic programs (and possibly others, too) expect ES to be 
	preserved by a call to an API function, so we have to save it.
	In GlobalFree() and FreeSelector(), we must clear CURRENT_STACK16->es 
	to prevent segfaults if ES contained the selector to be freed.

Sun Jul  9 20:21:20 1995  Jon Tombs  <jon@gtex02.us.es>

	* [*/*]
	Added missing prototypes to header files and relevant includes
	to reduce compile time warnings.

Sun Jul  9 18:32:56 1995  Michael Patra  <micky@marie.physik.tu-berlin.de>

	* [configure.in] [include/config.h] [*/Makefile.in]
	New configuration scheme based on autoconf.

Sat Jul  8 14:12:45 1995  Morten Welinder  <terra+@cs.cmu.edu>

	* [miscemu/ioports.c]
	Revamp to have only one in- and one out- variant, both really
 	implemented.

	* [miscemu/instr.c]
	INSTR_EmulateInstruction: Use new ioport interface.  Implement
 	string io.  Correct instruction pointer for 32-bit code.

	* [include/miscemu.h]
	Update port function prototypes.

	* [include/registers.h]
	Defined FS and GS.

Sat Jul  8 13:38:54 1995  Hans de Graaff  <graaff@twi72.twi.tudelft.nl>

	* [misc/dos_fs.c]
	ChopOffSlash(): A path consisting off a single slash is left
 	intact, and multiple slashes are all removed.
1995-07-29 13:09:43 +00:00

592 lines
16 KiB
C

/*
* text functions
*
* Copyright 1993, 1994 Alexandre Julliard
*
static char Copyright[] = "Copyright Alexandre Julliard, 1993, 1994";
*/
#include <stdlib.h>
#include <X11/Xatom.h>
#include "windows.h"
#include "dc.h"
#include "gdi.h"
#include "callback.h"
#include "metafile.h"
#include "stddebug.h"
/* #define DEBUG_TEXT */
#include "debug.h"
#define TAB 9
#define LF 10
#define CR 13
#define SPACE 32
#define PREFIX 38
#define SWAP_INT(a,b) { int t = a; a = b; b = t; }
static int tabstop = 8;
static int tabwidth;
static int spacewidth;
static int prefix_offset;
static char *TEXT_NextLine(HDC hdc, char *str, int *count, char *dest,
int *len, int width, WORD format)
{
/* Return next line of text from a string.
*
* hdc - handle to DC.
* str - string to parse into lines.
* count - length of str.
* dest - destination in which to return line.
* len - length of resultant line in dest in chars.
* width - maximum width of line in pixels.
* format - format type passed to DrawText.
*
* Returns pointer to next char in str after end of the line
* or NULL if end of str reached.
*/
int i = 0, j = 0, k;
int plen = 0;
int numspaces;
SIZE size;
int lasttab = 0;
int wb_i = 0, wb_j = 0, wb_count = 0;
while (*count)
{
switch (str[i])
{
case CR:
case LF:
if (!(format & DT_SINGLELINE))
{
if (str[i] == CR && str[i+1] == LF)
i++;
i++;
*len = j;
(*count)--;
return (&str[i]);
}
dest[j++] = str[i++];
if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
(format & DT_WORDBREAK))
{
if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
break;
case PREFIX:
if (!(format & DT_NOPREFIX))
{
prefix_offset = j;
i++;
}
else
{
dest[j++] = str[i++];
if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
{
if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
}
break;
case TAB:
if (format & DT_EXPANDTABS)
{
wb_i = ++i;
wb_j = j;
wb_count = *count;
if (!GetTextExtentPoint(hdc, &dest[lasttab], j - lasttab,
&size))
return NULL;
numspaces = (tabwidth - size.cx) / spacewidth;
for (k = 0; k < numspaces; k++)
dest[j++] = SPACE;
plen += tabwidth - size.cx;
lasttab = wb_j + numspaces;
}
else
{
dest[j++] = str[i++];
if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
(format & DT_WORDBREAK))
{
if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
}
break;
case SPACE:
dest[j++] = str[i++];
if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
(format & DT_WORDBREAK))
{
wb_i = i;
wb_j = j - 1;
wb_count = *count;
if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
break;
default:
dest[j++] = str[i++];
if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
(format & DT_WORDBREAK))
{
if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
}
(*count)--;
if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
{
if (plen > width)
{
if (format & DT_WORDBREAK)
{
if (wb_j)
{
*len = wb_j;
*count = wb_count - 1;
return (&str[wb_i]);
}
}
else
{
*len = j;
return (&str[i]);
}
}
}
}
*len = j;
return NULL;
}
/***********************************************************************
* DrawText (USER.85)
*/
int DrawText( HDC hdc, LPSTR str, int count, LPRECT rect, WORD flags )
{
SIZE size;
char *strPtr;
static char line[1024];
int len, lh;
int prefix_x = 0;
int prefix_end = 0;
TEXTMETRIC tm;
int x = rect->left, y = rect->top;
int width = rect->right - rect->left;
int max_width = 0;
dprintf_text(stddeb,"DrawText: '%s', %d , [(%d,%d),(%d,%d)]\n", str, count,
rect->left, rect->top, rect->right, rect->bottom);
if (count == -1) count = strlen(str);
strPtr = str;
GetTextMetrics(hdc, &tm);
if (flags & DT_EXTERNALLEADING)
lh = tm.tmHeight + tm.tmExternalLeading;
else
lh = tm.tmHeight;
if (flags & DT_TABSTOP)
tabstop = flags >> 8;
if (flags & DT_EXPANDTABS)
{
GetTextExtentPoint(hdc, " ", 1, &size);
spacewidth = size.cx;
GetTextExtentPoint(hdc, "o", 1, &size);
tabwidth = size.cx * tabstop;
}
do
{
prefix_offset = -1;
strPtr = TEXT_NextLine(hdc, strPtr, &count, line, &len, width, flags);
if (prefix_offset != -1)
{
GetTextExtentPoint(hdc, line, prefix_offset, &size);
prefix_x = size.cx;
GetTextExtentPoint(hdc, line, prefix_offset + 1, &size);
prefix_end = size.cx - 1;
}
if (!GetTextExtentPoint(hdc, line, len, &size)) return 0;
if (flags & DT_CENTER) x = (rect->left + rect->right -
size.cx) / 2;
else if (flags & DT_RIGHT) x = rect->right - size.cx;
if (flags & DT_SINGLELINE)
{
if (flags & DT_VCENTER) y = rect->top +
(rect->bottom - rect->top) / 2 - size.cy / 2;
else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
}
if (!(flags & DT_CALCRECT))
{
if (!ExtTextOut( hdc, x, y, (flags & DT_NOCLIP) ? 0 : ETO_CLIPPED,
rect, line, len, NULL )) return 0;
}
else if (size.cx > max_width)
max_width = size.cx;
if (prefix_offset != -1)
{
HPEN hpen = CreatePen( PS_SOLID, 1, GetTextColor(hdc) );
HPEN oldPen = SelectObject( hdc, hpen );
MoveTo(hdc, x + prefix_x, y + tm.tmAscent + 1 );
LineTo(hdc, x + prefix_end, y + tm.tmAscent + 1 );
SelectObject( hdc, oldPen );
DeleteObject( hpen );
}
y += lh;
if (strPtr)
{
if (!(flags & DT_NOCLIP) && !(flags & DT_CALCRECT))
{
if (y > rect->bottom - lh)
break;
}
}
}
while (strPtr);
if (flags & DT_CALCRECT)
{
rect->right = rect->left + max_width;
rect->bottom = y;
}
return 1;
}
/***********************************************************************
* ExtTextOut (GDI.351)
*/
BOOL ExtTextOut( HDC hdc, short x, short y, WORD flags, LPRECT lprect,
LPSTR str, WORD count, LPINT lpDx )
{
int dir, ascent, descent, i;
XCharStruct info;
XFontStruct *font;
RECT rect;
DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
if (!dc)
{
dc = (DC *)GDI_GetObjPtr( hdc, METAFILE_DC_MAGIC );
if (!dc) return FALSE;
MF_TextOut( dc, x, y, str, count );
return TRUE;
}
if (!DC_SetupGCForText( dc )) return TRUE;
font = dc->u.x.font.fstruct;
dprintf_text(stddeb,"ExtTextOut: %d,%d '%*.*s', %d flags=%d\n",
x, y, count, count, str, count, flags);
if (lprect != NULL) {
dprintf_text(stddeb, "rect %d %d %d %d\n",
lprect->left, lprect->top, lprect->right, lprect->bottom );
}
/* Setup coordinates */
if (dc->w.textAlign & TA_UPDATECP)
{
x = dc->w.CursPosX;
y = dc->w.CursPosY;
}
x = XLPTODP( dc, x );
y = YLPTODP( dc, y );
if (flags & (ETO_OPAQUE | ETO_CLIPPED)) /* There's a rectangle */
{
rect.left = XLPTODP( dc, lprect->left );
rect.right = XLPTODP( dc, lprect->right );
rect.top = YLPTODP( dc, lprect->top );
rect.bottom = YLPTODP( dc, lprect->bottom );
if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
}
/* Draw the rectangle */
if (flags & ETO_OPAQUE)
{
XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
rect.right-rect.left, rect.bottom-rect.top );
}
if (!count) return TRUE; /* Nothing more to do */
/* Compute text starting position */
XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
if (lpDx) for (i = 0; i < count; i++) info.width += lpDx[i];
switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
{
case TA_LEFT:
if (dc->w.textAlign & TA_UPDATECP)
dc->w.CursPosX = XDPTOLP( dc, x + info.width );
break;
case TA_RIGHT:
x -= info.width;
if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
break;
case TA_CENTER:
x -= info.width / 2;
break;
}
switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
{
case TA_TOP:
y += font->ascent;
break;
case TA_BOTTOM:
y -= font->descent;
break;
case TA_BASELINE:
break;
}
/* Set the clip region */
if (flags & ETO_CLIPPED)
{
SaveVisRgn( hdc );
IntersectVisRect( hdc, rect.left, rect.top, rect.right, rect.bottom );
}
/* Draw the text background if necessary */
if (dc->w.backgroundMode != TRANSPARENT)
{
/* If rectangle is opaque and clipped, do nothing */
if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
{
/* Only draw if rectangle is not opaque or if some */
/* text is outside the rectangle */
if (!(flags & ETO_OPAQUE) ||
(x < rect.left) ||
(x + info.width >= rect.right) ||
(y-font->ascent < rect.top) ||
(y+font->descent >= rect.bottom))
{
XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + x,
dc->w.DCOrgY + y - font->ascent,
info.width,
font->ascent + font->descent );
}
}
}
/* Draw the text */
XSetForeground( display, dc->u.x.gc, dc->w.textPixel );
if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
{
XDrawString( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
}
else /* Now the fun begins... */
{
XTextItem *items, *pitem;
items = malloc( count * sizeof(XTextItem) );
for (i = 0, pitem = items; i < count; i++, pitem++)
{
pitem->chars = str + i;
pitem->nchars = 1;
pitem->font = None;
if (i == 0)
{
pitem->delta = 0;
continue; /* First iteration -> no delta */
}
pitem->delta = dc->w.charExtra;
if (str[i] == (char)dc->u.x.font.metrics.tmBreakChar)
pitem->delta += dc->w.breakExtra;
if (lpDx)
{
INT width;
GetCharWidth( hdc, str[i], str[i], &width );
pitem->delta += lpDx[i-1] - width;
}
}
XDrawText( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, count );
free( items );
}
/* Draw underline and strike-out if needed */
if (dc->u.x.font.metrics.tmUnderlined)
{
long linePos, lineWidth;
if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
linePos = font->descent-1;
if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
lineWidth = 0;
else if (lineWidth == 1) lineWidth = 0;
XSetLineAttributes( display, dc->u.x.gc, lineWidth,
LineSolid, CapRound, JoinBevel );
XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
}
if (dc->u.x.font.metrics.tmStruckOut)
{
long lineAscent, lineDescent;
if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
lineAscent = font->ascent / 3;
if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
lineDescent = -lineAscent;
XSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
LineSolid, CapRound, JoinBevel );
XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
}
if (flags & ETO_CLIPPED) RestoreVisRgn( hdc );
return TRUE;
}
/***********************************************************************
* TextOut (GDI.33)
*/
BOOL TextOut( HDC hdc, short x, short y, LPSTR str, short count )
{
return ExtTextOut( hdc, x, y, 0, NULL, str, count, NULL );
}
/***********************************************************************
* GrayString (USER.185)
*/
BOOL GrayString(HDC hdc, HBRUSH hbr, FARPROC gsprc, LPARAM lParam,
INT cch, INT x, INT y, INT cx, INT cy)
{
int s, current_color;
if (gsprc) {
return CallGrayStringProc(gsprc, hdc, lParam,
cch ? cch : lstrlen((LPCSTR) lParam) );
} else {
current_color = GetTextColor(hdc);
SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT) );
s = TextOut(hdc, x, y, (LPSTR) lParam,
cch ? cch : lstrlen((LPCSTR) lParam) );
SetTextColor(hdc, current_color);
return s;
}
}
/***********************************************************************
* TEXT_TabbedTextOut
*
* Helper function for TabbedTextOut() and GetTabbedTextExtent().
* Note: this doesn't work too well for text-alignment modes other
* than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
*/
LONG TEXT_TabbedTextOut( HDC hdc, int x, int y, LPSTR lpstr, int count,
int cTabStops, LPINT lpTabPos, int nTabOrg,
BOOL fDisplayText)
{
WORD defWidth;
DWORD extent;
int i, tabPos = 0;
if (cTabStops == 1) defWidth = *lpTabPos;
else
{
TEXTMETRIC tm;
GetTextMetrics( hdc, &tm );
defWidth = 8 * tm.tmAveCharWidth;
}
while (count > 0)
{
for (i = 0; i < count; i++)
if (lpstr[i] == '\t') break;
extent = GetTextExtent( hdc, lpstr, i );
while ((cTabStops > 0) && (nTabOrg + *lpTabPos < x + LOWORD(extent)))
{
lpTabPos++;
cTabStops--;
}
if (lpstr[i] != '\t')
tabPos = x + LOWORD(extent);
else if (cTabStops > 0)
tabPos = nTabOrg + *lpTabPos;
else
tabPos = (x + LOWORD(extent) + defWidth - 1) / defWidth * defWidth;
if (fDisplayText)
{
RECT r;
SetRect( &r, x, y, tabPos, y+HIWORD(extent) );
ExtTextOut( hdc, x, y,
GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
&r, lpstr, i, NULL );
}
x = tabPos;
count -= i+1;
lpstr += i+1;
}
return tabPos;
}
/***********************************************************************
* TabbedTextOut (USER.196)
*/
LONG TabbedTextOut( HDC hdc, short x, short y, LPSTR lpstr, short count,
short cTabStops, LPINT lpTabPos, short nTabOrg )
{
dprintf_text( stddeb, "TabbedTextOut: %x %d,%d '%*.*s' %d\n",
hdc, x, y, count, count, lpstr, count );
return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
lpTabPos, nTabOrg, TRUE );
}
/***********************************************************************
* GetTabbedTextExtent (USER.197)
*/
DWORD GetTabbedTextExtent( HDC hdc, LPSTR lpstr, int count,
int cTabStops, LPINT lpTabPos )
{
dprintf_text( stddeb, "GetTabbedTextExtent: %x '%*.*s' %d\n",
hdc, count, count, lpstr, count );
return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
lpTabPos, 0, FALSE );
}