4 * Copyright 1993, 1994 Alexandre Julliard
7 * 1. DrawText functions
8 * 2. GrayString functions
9 * 3. TabbedText functions
16 #include "wine/winuser16.h"
17 #include "wine/unicode.h"
22 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(text);
26 #define countof(a) (sizeof(a)/sizeof(a[0]))
28 /*********************************************************************
33 * How many buffers to use
34 * While processing in DrawText there are potentially three different forms
35 * of the text that need to be held. How are they best held?
36 * 1. The original text is needed, of course, to see what to display.
37 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
39 * 3. The buffered text that is about to be displayed e.g. the current line.
40 * Typically this will exclude the ampersands used for prefixing etc.
43 * a. If the buffered text to be displayed includes the ampersands then
44 * we will need special measurement and draw functions that will ignore
45 * the ampersands (e.g. by copying to a buffer without the prefix and
46 * then using the normal forms). This may involve less space but may
47 * require more processing. e.g. since a line containing tabs may
48 * contain several underlined characters either we need to carry around
49 * a list of prefix locations or we may need to locate them several
51 * b. If we actually directly modify the "original text" as we go then we
52 * will need some special "caching" to handle the fact that when we
53 * ellipsify the text the ellipsis may modify the next line of text,
54 * which we have not yet processed. (e.g. ellipsification of a W at the
55 * end of a line will overwrite the W, the \n and the first character of
56 * the next line, and a \0 will overwrite the second. Try it!!)
58 * Option 1. Three separate storages. (To be implemented)
59 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
60 * the edited string in some form, either as the string itself or as some
61 * sort of "edit list" to be applied just before returning.
62 * Use a buffer that holds the ellipsified current line sans ampersands
63 * and accept the need occasionally to recalculate the prefixes (if
64 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
73 #define ELLIPSIS "..."
74 #define FORWARD_SLASH '/'
75 #define BACK_SLASH '\\'
77 static const WCHAR SPACEW[] = {' ', 0};
78 static const WCHAR oW[] = {'o', 0};
79 static const WCHAR ELLIPSISW[] = {'.','.','.', 0};
80 static const WCHAR FORWARD_SLASHW[] = {'/', 0};
81 static const WCHAR BACK_SLASHW[] = {'\\', 0};
85 static int spacewidth;
86 static int prefix_offset;
88 /*********************************************************************
91 * Reanalyse the text to find the prefixed character. This is called when
92 * wordbreaking or ellipsification has shortened the string such that the
93 * previously noted prefixed character is no longer visible.
96 * str [in] The original string segment (including all characters)
97 * n1 [in] The number of characters visible before the path ellipsis
98 * n2 [in] The number of characters replaced by the path ellipsis
99 * ne [in] The number of characters in the path ellipsis, ignored if
101 * n3 [in] The number of characters visible after the path ellipsis
104 * The prefix offset within the new string segment (the one that contains the
105 * ellipses and does not contain the prefix characters) (-1 if none)
108 * We know that n1+n2+n3 must be strictly less than the length of the segment
109 * (because otherwise there would be no need to call this function)
112 static int TEXT_Reprefix (const WCHAR *str, unsigned int n1, unsigned int n2,
113 unsigned int ne, unsigned int n3)
117 unsigned int n = n1 + n2 + n3;
123 /* Reached the path ellipsis; jump over it */
126 if (!n3) break; /* Nothing after the path ellipsis */
128 if (*str++ == PREFIX)
130 result = (i < n1) ? i : i - n2 + ne;
139 /*********************************************************************
140 * Return next line of text from a string.
142 * hdc - handle to DC.
143 * str - string to parse into lines.
144 * count - length of str.
145 * dest - destination in which to return line.
146 * len - dest buffer size in chars on input, copied length into dest on output.
147 * width - maximum width of line in pixels.
148 * format - format type passed to DrawText.
150 * Returns pointer to next char in str after end of the line
151 * or NULL if end of str reached.
154 * GetTextExtentPoint is used to get the width of each character,
155 * rather than GetCharABCWidth... So the whitespace between
156 * characters is ignored, and the reported len is too great.
158 static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count,
159 WCHAR *dest, int *len, int width, WORD format)
166 int wb_i = 0, wb_j = 0, wb_count = 0;
169 while (*count && j < maxl)
175 if (!(format & DT_SINGLELINE))
177 if ((*count > 1) && (str[i] == CR) && (str[i+1] == LF))
187 dest[j++] = str[i++];
188 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
189 (format & DT_WORDBREAK))
191 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
198 if (!(format & DT_NOPREFIX) && *count > 1)
200 if (str[++i] == PREFIX)
207 dest[j++] = str[i++];
208 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
209 (format & DT_WORDBREAK))
211 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
218 if (format & DT_EXPANDTABS)
224 if (!GetTextExtentPointW(hdc, &dest[lasttab], j - lasttab, &size))
227 numspaces = (tabwidth - size.cx) / spacewidth;
228 for (k = 0; k < numspaces; k++)
230 plen += tabwidth - size.cx;
231 lasttab = wb_j + numspaces;
235 dest[j++] = str[i++];
236 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
237 (format & DT_WORDBREAK))
239 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
247 dest[j++] = str[i++];
248 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
249 (format & DT_WORDBREAK))
254 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
261 dest[j++] = str[i++];
262 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
263 (format & DT_WORDBREAK))
265 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
272 if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
276 if (format & DT_WORDBREAK)
281 *count = wb_count - 1;
299 /***********************************************************************
300 * TEXT_DrawUnderscore
302 * Draw the underline under the prefixed character
305 * hdc [in] The handle of the DC for drawing
306 * x [in] The x location of the line segment (logical coordinates)
307 * y [in] The y location of where the underscore should appear
308 * (logical coordinates)
309 * str [in] The text of the line segment
310 * offset [in] The offset of the underscored character within str
313 static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset)
321 GetTextExtentPointW (hdc, str, offset, &size);
322 prefix_x = x + size.cx;
323 GetTextExtentPointW (hdc, str, offset+1, &size);
324 prefix_end = x + size.cx - 1;
325 /* The above method may eventually be slightly wrong due to kerning etc. */
327 hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc));
328 oldPen = SelectObject (hdc, hpen);
329 MoveToEx (hdc, prefix_x, y, NULL);
330 LineTo (hdc, prefix_end, y);
331 SelectObject (hdc, oldPen);
335 /***********************************************************************
336 * DrawTextExW (USER32.@)
338 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
339 * is not quite complete, especially with regard to \0. We will assume that
340 * the returned string could have a length of up to i_count+3 and also have
341 * a trailing \0 (which would be 4 more than a not-null-terminated string but
342 * 3 more than a null-terminated string). If this is not so then increase
343 * the allowance in DrawTextExA.
345 #define MAX_STATIC_BUFFER 1024
346 INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
347 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
351 static WCHAR line[MAX_STATIC_BUFFER];
352 int len, lh, count=i_count;
354 int lmargin = 0, rmargin = 0;
355 int x = rect->left, y = rect->top;
356 int width = rect->right - rect->left;
359 TRACE("%s, %d , [(%d,%d),(%d,%d)]\n", debugstr_wn (str, count), count,
360 rect->left, rect->top, rect->right, rect->bottom);
362 if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
363 dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin);
366 if (count == -1) count = strlenW(str);
367 if (count == 0) return 0;
370 GetTextMetricsW(hdc, &tm);
371 if (flags & DT_EXTERNALLEADING)
372 lh = tm.tmHeight + tm.tmExternalLeading;
378 lmargin = dtp->iLeftMargin * tm.tmAveCharWidth;
379 rmargin = dtp->iRightMargin * tm.tmAveCharWidth;
380 if (!(flags & (DT_CENTER | DT_RIGHT)))
382 dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */
385 tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8;
387 if (flags & DT_EXPANDTABS)
389 GetTextExtentPointW(hdc, SPACEW, 1, &size);
390 spacewidth = size.cx;
391 GetTextExtentPointW(hdc, oW, 1, &size);
392 tabwidth = size.cx * tabstop;
395 if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
400 len = MAX_STATIC_BUFFER;
401 strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags);
403 if (!GetTextExtentPointW(hdc, line, len, &size)) return 0;
404 if (flags & DT_CENTER) x = (rect->left + rect->right -
406 else if (flags & DT_RIGHT) x = rect->right - size.cx;
408 if (flags & DT_SINGLELINE)
410 if (flags & DT_VCENTER) y = rect->top +
411 (rect->bottom - rect->top) / 2 - size.cy / 2;
412 else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
415 if ((flags & DT_SINGLELINE) && size.cx > width &&
416 (flags & (DT_PATH_ELLIPSIS | DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)))
418 WCHAR swapStr[countof(line)];
419 WCHAR* fnameDelim = NULL;
420 int totalLen = i_count >= 0 ? i_count : strlenW(str);
421 int fnameLen = totalLen;
422 int old_prefix_offset = prefix_offset;
423 int len_before_ellipsis;
424 int len_after_ellipsis;
426 /* allow room for '...' */
427 count = min(totalLen+3, countof(line)-3);
429 if (flags & DT_WORD_ELLIPSIS)
430 flags |= DT_WORDBREAK;
432 if (flags & DT_PATH_ELLIPSIS)
434 WCHAR* lastBkSlash = NULL;
435 WCHAR* lastFwdSlash = NULL;
436 strncpyW(line, str, totalLen);
437 line[totalLen] = '\0';
438 lastBkSlash = strrchrW(line, BACK_SLASHW[0]);
439 lastFwdSlash = strrchrW(line, FORWARD_SLASHW[0]);
440 fnameDelim = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash;
443 fnameLen = &line[totalLen] - fnameDelim;
445 fnameDelim = (WCHAR*)str;
447 strcpyW(swapStr, ELLIPSISW);
448 strncpyW(swapStr+strlenW(swapStr), fnameDelim, fnameLen);
449 swapStr[fnameLen+3] = '\0';
450 strncpyW(swapStr+strlenW(swapStr), str, totalLen - fnameLen);
451 swapStr[totalLen+3] = '\0';
453 else /* DT_END_ELLIPSIS | DT_WORD_ELLIPSIS */
455 strcpyW(swapStr, ELLIPSISW);
456 strncpyW(swapStr+strlenW(swapStr), str, totalLen);
459 len = MAX_STATIC_BUFFER;
460 TEXT_NextLineW(hdc, swapStr, &count, line, &len, width, flags);
461 prefix_offset = old_prefix_offset;
463 /* if only the ELLIPSIS will fit, just let it be clipped */
465 GetTextExtentPointW(hdc, line, len, &size);
468 * NextLine uses GetTextExtentPoint for each character,
469 * rather than GetCharABCWidth... So the whitespace between
470 * characters is ignored in the width measurement, and the
471 * reported len is too great. To compensate, we must get
472 * the width of the entire line and adjust len accordingly.
474 while ((size.cx > width) && (len > 3))
477 GetTextExtentPointW(hdc, line, len, &size);
480 if (fnameLen < len-3) /* some of the path will fit */
482 len_before_ellipsis = len-3-fnameLen;
483 len_after_ellipsis = fnameLen;
484 /* put the ELLIPSIS between the path and filename */
485 strncpyW(swapStr, &line[fnameLen+3], len_before_ellipsis);
486 swapStr[len_before_ellipsis] = '\0';
487 strcatW(swapStr, ELLIPSISW);
488 strncpyW(swapStr+strlenW(swapStr), &line[3], fnameLen);
492 /* move the ELLIPSIS to the end */
493 len_before_ellipsis = len-3;
494 len_after_ellipsis = 0;
495 strncpyW(swapStr, &line[3], len_before_ellipsis);
496 swapStr[len_before_ellipsis] = '\0';
497 strcpyW(swapStr+strlenW(swapStr), ELLIPSISW);
500 strncpyW(line, swapStr, len);
504 if (prefix_offset >= 0 &&
505 prefix_offset >= len_before_ellipsis)
506 prefix_offset = TEXT_Reprefix (str, len_before_ellipsis, strlenW(str)-3-len_before_ellipsis-len_after_ellipsis, 3, len_after_ellipsis);
508 if (flags & DT_MODIFYSTRING)
509 strcpyW(str, swapStr);
511 if (!(flags & DT_CALCRECT))
513 if (!ExtTextOutW( hdc, x, y,
514 ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) |
515 ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0),
516 rect, line, len, NULL )) return 0;
517 if (prefix_offset != -1)
518 TEXT_DrawUnderscore (hdc, x, y + tm.tmAscent + 1, line, prefix_offset);
520 else if (size.cx > max_width)
526 if (!(flags & DT_NOCLIP))
528 if (y > rect->bottom - lh)
533 dtp->uiLengthDrawn += len;
537 if (flags & DT_CALCRECT)
539 rect->right = rect->left + max_width;
542 rect->right += lmargin + rmargin;
544 return y - rect->top;
547 /***********************************************************************
548 * DrawTextExA (USER32.@)
550 * If DT_MODIFYSTRING is specified then there must be room for up to
551 * 4 extra characters. We take great care about just how much modified
554 INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
555 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
565 if (count == -1) count = strlen(str);
566 if (!count) return 0;
567 wcount = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
570 if (flags & DT_MODIFYSTRING)
575 wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
578 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, wcount );
579 if (flags & DT_MODIFYSTRING)
580 for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE;
581 /* Initialise the extra characters so that we can see which ones
582 * change. U+FFFE is guaranteed to be not a unicode character and
583 * so will not be generated by DrawTextEx itself.
585 ret = DrawTextExW( hdc, wstr, wcount, rect, flags, NULL );
586 if (flags & DT_MODIFYSTRING)
588 /* Unfortunately the returned string may contain multiple \0s
589 * and so we need to measure it ourselves.
591 for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
592 WideCharToMultiByte( CP_ACP, 0, wstr, wcount, str, amax, NULL, NULL );
594 HeapFree(GetProcessHeap(), 0, wstr);
599 /***********************************************************************
600 * DrawTextW (USER32.@)
602 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
606 memset (&dtp, 0, sizeof(dtp));
607 if (flags & DT_TABSTOP)
609 dtp.iTabLength = (flags >> 8) && 0xff;
612 return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
615 /***********************************************************************
616 * DrawTextA (USER32.@)
618 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
622 memset (&dtp, 0, sizeof(dtp));
623 if (flags & DT_TABSTOP)
625 dtp.iTabLength = (flags >> 8) && 0xff;
628 return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
631 /***********************************************************************
634 INT16 WINAPI DrawText16( HDC16 hdc, LPCSTR str, INT16 count, LPRECT16 rect, UINT16 flags )
641 CONV_RECT16TO32( rect, &rect32 );
642 ret = DrawTextA( hdc, str, count, &rect32, flags );
643 CONV_RECT32TO16( &rect32, rect );
645 else ret = DrawTextA( hdc, str, count, NULL, flags);
650 /***********************************************************************
652 * GrayString functions
655 /* ### start build ### */
656 extern WORD CALLBACK TEXT_CallTo16_word_wlw(GRAYSTRINGPROC16,WORD,LONG,WORD);
657 /* ### stop build ### */
659 struct gray_string_info
661 GRAYSTRINGPROC16 proc;
665 /* callback for 16-bit gray string proc */
666 static BOOL CALLBACK gray_string_callback( HDC hdc, LPARAM param, INT len )
668 const struct gray_string_info *info = (struct gray_string_info *)param;
669 return TEXT_CallTo16_word_wlw( info->proc, hdc, info->param, len );
672 /***********************************************************************
675 * FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
676 * heap and we can guarantee that the handles fit in an INT16. We have to
677 * rethink the strategy once the migration to NT handles is complete.
678 * We are going to get a lot of code-duplication once this migration is
682 static BOOL TEXT_GrayString(HDC hdc, HBRUSH hb, GRAYSTRINGPROC fn, LPARAM lp, INT len,
683 INT x, INT y, INT cx, INT cy, BOOL unicode, BOOL _32bit)
685 HBITMAP hbm, hbmsave;
693 if(!hdc) return FALSE;
694 if (!(memdc = CreateCompatibleDC(hdc))) return FALSE;
699 slen = lstrlenW((LPCWSTR)lp);
701 slen = strlen((LPCSTR)lp);
703 slen = strlen(MapSL(lp));
706 if((cx == 0 || cy == 0) && slen != -1)
710 GetTextExtentPoint32W(hdc, (LPCWSTR)lp, slen, &s);
712 GetTextExtentPoint32A(hdc, (LPCSTR)lp, slen, &s);
714 GetTextExtentPoint32A(hdc, MapSL(lp), slen, &s);
715 if(cx == 0) cx = s.cx;
716 if(cy == 0) cy = s.cy;
719 hbm = CreateBitmap(cx, cy, 1, 1, NULL);
720 hbmsave = (HBITMAP)SelectObject(memdc, hbm);
721 hbsave = SelectObject( memdc, GetStockObject(BLACK_BRUSH) );
722 PatBlt( memdc, 0, 0, cx, cy, PATCOPY );
723 SelectObject( memdc, hbsave );
724 SetTextColor(memdc, RGB(255, 255, 255));
725 SetBkColor(memdc, RGB(0, 0, 0));
726 hfsave = (HFONT)SelectObject(memdc, GetCurrentObject(hdc, OBJ_FONT));
731 retval = fn(memdc, lp, slen);
733 retval = (BOOL)((BOOL16)((GRAYSTRINGPROC16)fn)((HDC16)memdc, lp, (INT16)slen));
738 TextOutW(memdc, 0, 0, (LPCWSTR)lp, slen);
740 TextOutA(memdc, 0, 0, (LPCSTR)lp, slen);
742 TextOutA(memdc, 0, 0, MapSL(lp), slen);
745 SelectObject(memdc, hfsave);
748 * Windows doc says that the bitmap isn't grayed when len == -1 and
749 * the callback function returns FALSE. However, testing this on
750 * win95 showed otherwise...
752 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
753 if(retval || len != -1)
756 hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
757 PatBlt(memdc, 0, 0, cx, cy, 0x000A0329);
758 SelectObject(memdc, hbsave);
761 if(hb) hbsave = (HBRUSH)SelectObject(hdc, hb);
762 fg = SetTextColor(hdc, RGB(0, 0, 0));
763 bg = SetBkColor(hdc, RGB(255, 255, 255));
764 BitBlt(hdc, x, y, cx, cy, memdc, 0, 0, 0x00E20746);
765 SetTextColor(hdc, fg);
767 if(hb) SelectObject(hdc, hbsave);
769 SelectObject(memdc, hbmsave);
776 /***********************************************************************
777 * GrayString (USER.185)
779 BOOL16 WINAPI GrayString16( HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc,
780 LPARAM lParam, INT16 cch, INT16 x, INT16 y,
783 struct gray_string_info info;
785 if (!gsprc) return TEXT_GrayString(hdc, hbr, NULL, lParam, cch, x, y, cx, cy, FALSE, FALSE);
788 return TEXT_GrayString( hdc, hbr, gray_string_callback, (LPARAM)&info,
789 cch, x, y, cx, cy, FALSE, FALSE);
793 /***********************************************************************
794 * GrayStringA (USER32.@)
796 BOOL WINAPI GrayStringA( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
797 LPARAM lParam, INT cch, INT x, INT y,
800 return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
805 /***********************************************************************
806 * GrayStringW (USER32.@)
808 BOOL WINAPI GrayStringW( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
809 LPARAM lParam, INT cch, INT x, INT y,
812 return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
816 /***********************************************************************
818 * TabbedText functions
821 /***********************************************************************
824 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
825 * Note: this doesn't work too well for text-alignment modes other
826 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
828 static LONG TEXT_TabbedTextOut( HDC hdc, INT x, INT y, LPCSTR lpstr,
829 INT count, INT cTabStops, const INT16 *lpTabPos16,
830 const INT *lpTabPos32, INT nTabOrg,
843 defWidth = lpTabPos32 ? *lpTabPos32 : *lpTabPos16;
849 GetTextMetricsA( hdc, &tm );
850 defWidth = 8 * tm.tmAveCharWidth;
855 for (i = 0; i < count; i++)
856 if (lpstr[i] == '\t') break;
857 GetTextExtentPointA( hdc, lpstr, i, &extent );
860 while ((cTabStops > 0) &&
861 (nTabOrg + *lpTabPos32 <= x + extent.cx))
869 while ((cTabStops > 0) &&
870 (nTabOrg + *lpTabPos16 <= x + extent.cx))
877 tabPos = x + extent.cx;
878 else if (cTabStops > 0)
879 tabPos = nTabOrg + (lpTabPos32 ? *lpTabPos32 : *lpTabPos16);
881 tabPos = nTabOrg + ((x + extent.cx - nTabOrg) / defWidth + 1) * defWidth;
888 r.bottom = y + extent.cy;
889 ExtTextOutA( hdc, x, y,
890 GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
891 &r, lpstr, i, NULL );
897 return MAKELONG(tabPos - start, extent.cy);
901 /***********************************************************************
902 * TabbedTextOut (USER.196)
904 LONG WINAPI TabbedTextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR lpstr,
905 INT16 count, INT16 cTabStops,
906 const INT16 *lpTabPos, INT16 nTabOrg )
908 TRACE("%04x %d,%d %s %d\n", hdc, x, y, debugstr_an(lpstr,count), count );
909 return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
910 lpTabPos, NULL, nTabOrg, TRUE );
914 /***********************************************************************
915 * TabbedTextOutA (USER32.@)
917 LONG WINAPI TabbedTextOutA( HDC hdc, INT x, INT y, LPCSTR lpstr, INT count,
918 INT cTabStops, const INT *lpTabPos, INT nTabOrg )
920 TRACE("%04x %d,%d %s %d\n", hdc, x, y, debugstr_an(lpstr,count), count );
921 return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
922 NULL, lpTabPos, nTabOrg, TRUE );
926 /***********************************************************************
927 * TabbedTextOutW (USER32.@)
929 LONG WINAPI TabbedTextOutW( HDC hdc, INT x, INT y, LPCWSTR str, INT count,
930 INT cTabStops, const INT *lpTabPos, INT nTabOrg )
935 UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
937 acount = WideCharToMultiByte(codepage,0,str,count,NULL,0,NULL,NULL);
938 p = HeapAlloc( GetProcessHeap(), 0, acount );
939 if(p == NULL) return 0; /* FIXME: is this the correct return on failure */
940 acount = WideCharToMultiByte(codepage,0,str,count,p,acount,NULL,NULL);
941 ret = TabbedTextOutA( hdc, x, y, p, acount, cTabStops, lpTabPos, nTabOrg );
942 HeapFree( GetProcessHeap(), 0, p );
947 /***********************************************************************
948 * GetTabbedTextExtent (USER.197)
950 DWORD WINAPI GetTabbedTextExtent16( HDC16 hdc, LPCSTR lpstr, INT16 count,
951 INT16 cTabStops, const INT16 *lpTabPos )
953 TRACE("%04x %s %d\n", hdc, debugstr_an(lpstr,count), count );
954 return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
955 lpTabPos, NULL, 0, FALSE );
959 /***********************************************************************
960 * GetTabbedTextExtentA (USER32.@)
962 DWORD WINAPI GetTabbedTextExtentA( HDC hdc, LPCSTR lpstr, INT count,
963 INT cTabStops, const INT *lpTabPos )
965 TRACE("%04x %s %d\n", hdc, debugstr_an(lpstr,count), count );
966 return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
967 NULL, lpTabPos, 0, FALSE );
971 /***********************************************************************
972 * GetTabbedTextExtentW (USER32.@)
974 DWORD WINAPI GetTabbedTextExtentW( HDC hdc, LPCWSTR lpstr, INT count,
975 INT cTabStops, const INT *lpTabPos )
980 UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
982 acount = WideCharToMultiByte(codepage,0,lpstr,count,NULL,0,NULL,NULL);
983 p = HeapAlloc( GetProcessHeap(), 0, acount );
984 if(p == NULL) return 0; /* FIXME: is this the correct failure value? */
985 acount = WideCharToMultiByte(codepage,0,lpstr,count,p,acount,NULL,NULL);
986 ret = GetTabbedTextExtentA( hdc, p, acount, cTabStops, lpTabPos );
987 HeapFree( GetProcessHeap(), 0, p );