Ensure that the whole modified text is returned from DrawTextExA.
[wine] / dlls / user / text.c
1 /*
2  * USER text functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  *
6  * Contains 
7  *   1.  DrawText functions
8  *   2.  GrayString functions
9  *   3.  TabbedText functions
10  */
11
12 #include <string.h>
13
14 #include "windef.h"
15 #include "wingdi.h"
16 #include "wine/winuser16.h"
17 #include "wine/unicode.h"
18 #include "winbase.h"
19 #include "winerror.h"
20 #include "winnls.h"
21 #include "user.h"
22 #include "debugtools.h"
23
24 DEFAULT_DEBUG_CHANNEL(text);
25
26 #define countof(a) (sizeof(a)/sizeof(a[0]))
27
28 /*********************************************************************
29  *
30  *            DrawText functions
31  *
32  * Design issues
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
38  *        in effect.
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.
41  *
42  *     Complications.
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
50  *        times.
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!!)
57  *
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)
65  */
66
67 #define TAB     9
68 #define LF     10
69 #define CR     13
70 #define SPACE  32
71 #define PREFIX 38
72
73 #define ELLIPSIS "..."
74 #define FORWARD_SLASH '/'
75 #define BACK_SLASH '\\'
76
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};
82
83 static int tabstop;
84 static int tabwidth;
85 static int spacewidth;
86 static int prefix_offset;
87
88 /*********************************************************************
89  *                      TEXT_Reprefix
90  *
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.
94  *
95  * Parameters
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
100  *              n2 is zero
101  *   n3         [in] The number of characters visible after the path ellipsis
102  *
103  * Return Values
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)
106  *
107  * Remarks
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)
110  */
111
112 static int TEXT_Reprefix (const WCHAR *str, unsigned int n1, unsigned int n2,
113                           unsigned int ne, unsigned int n3)
114 {
115     int result = -1;
116     unsigned int i = 0;
117     unsigned int n = n1 + n2 + n3;
118     if (!n2) ne = 0;
119     while (i < n)
120     {
121         if (i == n1)
122         {
123             /* Reached the path ellipsis; jump over it */
124             str += n2;
125             i += n2;
126             if (!n3) break; /* Nothing after the path ellipsis */
127         }
128         if (*str++ == PREFIX)
129         {
130             result = (i < n1) ? i : i - n2 + ne;
131             str++;
132         }
133         else;
134         i++;
135     }
136     return result;
137 }
138
139 /*********************************************************************
140  *  Return next line of text from a string.
141  * 
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.
149  *
150  * Returns pointer to next char in str after end of the line
151  * or NULL if end of str reached.
152  *
153  * FIXME:
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.
157  */
158 static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count,
159                                   WCHAR *dest, int *len, int width, WORD format)
160 {
161     int i = 0, j = 0, k;
162     int plen = 0;
163     int numspaces;
164     SIZE size;
165     int lasttab = 0;
166     int wb_i = 0, wb_j = 0, wb_count = 0;
167     int maxl = *len;
168
169     while (*count && j < maxl)
170     {
171         switch (str[i])
172         {
173         case CR:
174         case LF:
175             if (!(format & DT_SINGLELINE))
176             {
177                 if ((*count > 1) && (str[i] == CR) && (str[i+1] == LF))
178                 {
179                     (*count)--;
180                     i++;
181                 }
182                 i++;
183                 *len = j;
184                 (*count)--;
185                 return (&str[i]);
186             }
187             dest[j++] = str[i++];
188             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
189                 (format & DT_WORDBREAK))
190             {
191                 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
192                     return NULL;
193                 plen += size.cx;
194             }
195             break;
196             
197         case PREFIX:
198             if (!(format & DT_NOPREFIX) && *count > 1)
199                 {
200                 if (str[++i] == PREFIX)
201                     (*count)--;
202                 else {
203                     prefix_offset = j;
204                     break;
205                 }
206             }
207             dest[j++] = str[i++];
208             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
209                 (format & DT_WORDBREAK))
210             {
211                 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
212                     return NULL;
213                 plen += size.cx;
214             }
215             break;
216             
217         case TAB:
218             if (format & DT_EXPANDTABS)
219             {
220                 wb_i = ++i;
221                 wb_j = j;
222                 wb_count = *count;
223
224                 if (!GetTextExtentPointW(hdc, &dest[lasttab], j - lasttab, &size))
225                     return NULL;
226
227                 numspaces = (tabwidth - size.cx) / spacewidth;
228                 for (k = 0; k < numspaces; k++)
229                     dest[j++] = SPACE;
230                 plen += tabwidth - size.cx;
231                 lasttab = wb_j + numspaces;
232             }
233             else
234             {
235                 dest[j++] = str[i++];
236                 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
237                     (format & DT_WORDBREAK))
238                 {
239                     if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
240                         return NULL;
241                     plen += size.cx;
242                 }
243             }
244             break;
245
246         case SPACE:
247             dest[j++] = str[i++];
248             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
249                 (format & DT_WORDBREAK))
250             {
251                 wb_i = i;
252                 wb_j = j - 1;
253                 wb_count = *count;
254                 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
255                     return NULL;
256                 plen += size.cx;
257             }
258             break;
259
260         default:
261             dest[j++] = str[i++];
262             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
263                 (format & DT_WORDBREAK))
264             {
265                 if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
266                     return NULL;
267                 plen += size.cx;
268             }
269         }
270
271         (*count)--;
272         if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
273         {
274             if (plen > width)
275             {
276                 if (format & DT_WORDBREAK)
277                 {
278                     if (wb_j)
279                     {
280                         *len = wb_j;
281                         *count = wb_count - 1;
282                         return (&str[wb_i]);
283                     }
284                 }
285                 else
286                 {
287                     *len = j;
288                     return (&str[i]);
289                 }
290             }
291         }
292     }
293     
294     *len = j;
295     return NULL;
296 }
297
298
299 /***********************************************************************
300  *                      TEXT_DrawUnderscore
301  *
302  *  Draw the underline under the prefixed character
303  *
304  * Parameters
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
311  */
312
313 static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset)
314 {
315     int prefix_x;
316     int prefix_end;
317     SIZE size;
318     HPEN hpen;
319     HPEN oldPen;
320
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. */
326
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);
332     DeleteObject (hpen);
333 }
334
335 /***********************************************************************
336  *           DrawTextExW    (USER32.@)
337  *
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.
344  */
345 #define MAX_STATIC_BUFFER 1024
346 INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
347                         LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
348 {
349     SIZE size;
350     const WCHAR *strPtr;
351     static WCHAR line[MAX_STATIC_BUFFER];
352     int len, lh, count=i_count;
353     TEXTMETRICW tm;
354     int lmargin = 0, rmargin = 0;
355     int x = rect->left, y = rect->top;
356     int width = rect->right - rect->left;
357     int max_width = 0;
358
359     TRACE("%s, %d , [(%d,%d),(%d,%d)]\n", debugstr_wn (str, count), count,
360           rect->left, rect->top, rect->right, rect->bottom);
361
362    if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
363           dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin);
364
365     if (!str) return 0;
366     if (count == -1) count = strlenW(str);
367     if (count == 0) return 0;
368     strPtr = str;
369
370     GetTextMetricsW(hdc, &tm);
371     if (flags & DT_EXTERNALLEADING)
372         lh = tm.tmHeight + tm.tmExternalLeading;
373     else
374         lh = tm.tmHeight;
375
376     if (dtp)
377     {
378         lmargin = dtp->iLeftMargin * tm.tmAveCharWidth;
379         rmargin = dtp->iRightMargin * tm.tmAveCharWidth;
380         if (!(flags & (DT_CENTER | DT_RIGHT)))
381             x += lmargin;
382         dtp->uiLengthDrawn = 0;     /* This param RECEIVES number of chars processed */
383     }
384
385     tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8;
386
387     if (flags & DT_EXPANDTABS)
388     {
389         GetTextExtentPointW(hdc, SPACEW, 1, &size);
390         spacewidth = size.cx;
391         GetTextExtentPointW(hdc, oW, 1, &size);
392         tabwidth = size.cx * tabstop;
393     }
394
395     if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
396
397     do
398     {
399         prefix_offset = -1;
400         len = MAX_STATIC_BUFFER;
401         strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags);
402
403         if (!GetTextExtentPointW(hdc, line, len, &size)) return 0;
404         if (flags & DT_CENTER) x = (rect->left + rect->right -
405                                     size.cx) / 2;
406         else if (flags & DT_RIGHT) x = rect->right - size.cx;
407
408         if (flags & DT_SINGLELINE)
409         {
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;
413         }
414
415         if ((flags & DT_SINGLELINE) && size.cx > width &&
416             (flags & (DT_PATH_ELLIPSIS | DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)))
417         {
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;
425
426                     /* allow room for '...' */
427                     count = min(totalLen+3, countof(line)-3);
428
429                     if (flags & DT_WORD_ELLIPSIS)
430                         flags |= DT_WORDBREAK;
431
432                     if (flags & DT_PATH_ELLIPSIS)
433                     {
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;
441
442                         if (fnameDelim)
443                             fnameLen = &line[totalLen] - fnameDelim;
444                         else
445                             fnameDelim = (WCHAR*)str;
446
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';
452                     }
453                     else  /* DT_END_ELLIPSIS | DT_WORD_ELLIPSIS */
454                     {
455                         strcpyW(swapStr, ELLIPSISW);
456                         strncpyW(swapStr+strlenW(swapStr), str, totalLen);
457                     }
458
459                     len = MAX_STATIC_BUFFER;
460                     TEXT_NextLineW(hdc, swapStr, &count, line, &len, width, flags);
461                     prefix_offset = old_prefix_offset;
462
463                     /* if only the ELLIPSIS will fit, just let it be clipped */
464                     len = max(3, len);
465                     GetTextExtentPointW(hdc, line, len, &size);
466
467                     /* FIXME:
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.
473                     */
474                     while ((size.cx > width) && (len > 3))
475                     {
476                         line[--len] = '\0';
477                         GetTextExtentPointW(hdc, line, len, &size);
478                     }
479
480                     if (fnameLen < len-3) /* some of the path will fit */
481                     {
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);
489                     }
490                     else
491                     {
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);
498                     }
499
500                     strncpyW(line, swapStr, len);
501                     line[len] = '\0';
502                     strPtr = NULL;
503
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);
507
508                if (flags & DT_MODIFYSTRING)
509                     strcpyW(str, swapStr);
510         }
511         if (!(flags & DT_CALCRECT))
512         {
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);
519         }
520         else if (size.cx > max_width)
521             max_width = size.cx;
522
523         y += lh;
524         if (strPtr)
525         {
526             if (!(flags & DT_NOCLIP))
527             {
528                 if (y > rect->bottom - lh)
529                     break;
530             }
531         }
532         if (dtp)
533             dtp->uiLengthDrawn += len;
534     }
535     while (strPtr);
536
537     if (flags & DT_CALCRECT)
538     {
539         rect->right = rect->left + max_width;
540         rect->bottom = y;
541         if (dtp)
542             rect->right += lmargin + rmargin;
543     }
544     return y - rect->top;
545 }
546
547 /***********************************************************************
548  *           DrawTextExA    (USER32.@)
549  *
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
552  * string we return.
553  */
554 INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
555                         LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
556 {
557    WCHAR *wstr;
558    WCHAR *p;
559    INT ret = 0;
560    int i;
561    DWORD wcount;
562    DWORD wmax;
563    DWORD amax;
564
565    if (count == -1) count = strlen(str);
566    if (!count) return 0;
567    wcount = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
568    wmax = wcount;
569    amax = count;
570    if (flags & DT_MODIFYSTRING)
571    {
572         wmax += 4;
573         amax += 4;
574    }
575    wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
576    if (wstr)
577    {
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.
584             */
585        ret = DrawTextExW( hdc, wstr, wcount, rect, flags, NULL );
586        if (flags & DT_MODIFYSTRING)
587        {
588             /* Unfortunately the returned string may contain multiple \0s
589              * and so we need to measure it ourselves.
590              */
591             for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
592             WideCharToMultiByte( CP_ACP, 0, wstr, wcount, str, amax, NULL, NULL );
593        }
594        HeapFree(GetProcessHeap(), 0, wstr);
595    }
596    return ret;
597 }
598
599 /***********************************************************************
600  *           DrawTextW    (USER32.@)
601  */
602 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
603 {
604     DRAWTEXTPARAMS dtp;
605
606     memset (&dtp, 0, sizeof(dtp));
607     if (flags & DT_TABSTOP)
608     {
609         dtp.iTabLength = (flags >> 8) && 0xff;
610         flags &= 0xffff00ff;
611     }
612     return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
613 }
614
615 /***********************************************************************
616  *           DrawTextA    (USER32.@)
617  */
618 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
619 {
620     DRAWTEXTPARAMS dtp;
621
622     memset (&dtp, 0, sizeof(dtp));
623     if (flags & DT_TABSTOP)
624     {
625         dtp.iTabLength = (flags >> 8) && 0xff;
626         flags &= 0xffff00ff;
627     }
628     return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
629 }
630
631 /***********************************************************************
632  *           DrawText    (USER.85)
633  */
634 INT16 WINAPI DrawText16( HDC16 hdc, LPCSTR str, INT16 count, LPRECT16 rect, UINT16 flags )
635 {
636     INT16 ret;
637
638     if (rect)
639     {
640         RECT rect32;
641         CONV_RECT16TO32( rect, &rect32 );
642         ret = DrawTextA( hdc, str, count, &rect32, flags );
643         CONV_RECT32TO16( &rect32, rect );
644     }
645     else ret = DrawTextA( hdc, str, count, NULL, flags);
646     return ret;
647 }
648
649
650 /***********************************************************************
651  *
652  *           GrayString functions
653  */
654
655 /* ### start build ### */
656 extern WORD CALLBACK TEXT_CallTo16_word_wlw(GRAYSTRINGPROC16,WORD,LONG,WORD);
657 /* ### stop build ### */
658
659 struct gray_string_info
660 {
661     GRAYSTRINGPROC16 proc;
662     LPARAM           param;
663 };
664
665 /* callback for 16-bit gray string proc */
666 static BOOL CALLBACK gray_string_callback( HDC hdc, LPARAM param, INT len )
667 {
668     const struct gray_string_info *info = (struct gray_string_info *)param;
669     return TEXT_CallTo16_word_wlw( info->proc, hdc, info->param, len );
670 }
671
672 /***********************************************************************
673  *           TEXT_GrayString
674  *
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
679  * completed...
680  * 
681  */
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)
684 {
685     HBITMAP hbm, hbmsave;
686     HBRUSH hbsave;
687     HFONT hfsave;
688     HDC memdc;
689     int slen = len;
690     BOOL retval = TRUE;
691     COLORREF fg, bg;
692
693     if(!hdc) return FALSE;
694     if (!(memdc = CreateCompatibleDC(hdc))) return FALSE;
695
696     if(len == 0)
697     {
698         if(unicode)
699             slen = lstrlenW((LPCWSTR)lp);
700         else if(_32bit)
701             slen = strlen((LPCSTR)lp);
702         else
703             slen = strlen(MapSL(lp));
704     }
705
706     if((cx == 0 || cy == 0) && slen != -1)
707     {
708         SIZE s;
709         if(unicode)
710             GetTextExtentPoint32W(hdc, (LPCWSTR)lp, slen, &s);
711         else if(_32bit)
712             GetTextExtentPoint32A(hdc, (LPCSTR)lp, slen, &s);
713         else
714             GetTextExtentPoint32A(hdc, MapSL(lp), slen, &s);
715         if(cx == 0) cx = s.cx;
716         if(cy == 0) cy = s.cy;
717     }
718
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));
727
728     if(fn)
729     {
730         if(_32bit)
731             retval = fn(memdc, lp, slen);
732         else
733             retval = (BOOL)((BOOL16)((GRAYSTRINGPROC16)fn)((HDC16)memdc, lp, (INT16)slen));
734     }
735     else
736     {
737         if(unicode)
738             TextOutW(memdc, 0, 0, (LPCWSTR)lp, slen);
739         else if(_32bit)
740             TextOutA(memdc, 0, 0, (LPCSTR)lp, slen);
741         else
742             TextOutA(memdc, 0, 0, MapSL(lp), slen);
743     }
744
745     SelectObject(memdc, hfsave);
746
747 /*
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...
751 */
752 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
753     if(retval || len != -1)
754 #endif
755     {
756         hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
757         PatBlt(memdc, 0, 0, cx, cy, 0x000A0329);
758         SelectObject(memdc, hbsave);
759     }
760
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);
766     SetBkColor(hdc, bg);
767     if(hb) SelectObject(hdc, hbsave);
768
769     SelectObject(memdc, hbmsave);
770     DeleteObject(hbm);
771     DeleteDC(memdc);
772     return retval;
773 }
774
775
776 /***********************************************************************
777  *           GrayString   (USER.185)
778  */
779 BOOL16 WINAPI GrayString16( HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc,
780                             LPARAM lParam, INT16 cch, INT16 x, INT16 y,
781                             INT16 cx, INT16 cy )
782 {
783     struct gray_string_info info;
784
785     if (!gsprc) return TEXT_GrayString(hdc, hbr, NULL, lParam, cch, x, y, cx, cy, FALSE, FALSE);
786     info.proc  = gsprc;
787     info.param = lParam;
788     return TEXT_GrayString( hdc, hbr, gray_string_callback, (LPARAM)&info,
789                             cch, x, y, cx, cy, FALSE, FALSE);
790 }
791
792
793 /***********************************************************************
794  *           GrayStringA   (USER32.@)
795  */
796 BOOL WINAPI GrayStringA( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
797                          LPARAM lParam, INT cch, INT x, INT y,
798                          INT cx, INT cy )
799 {
800     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
801                            FALSE, TRUE);
802 }
803
804
805 /***********************************************************************
806  *           GrayStringW   (USER32.@)
807  */
808 BOOL WINAPI GrayStringW( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
809                          LPARAM lParam, INT cch, INT x, INT y,
810                          INT cx, INT cy )
811 {
812     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
813                            TRUE, TRUE);
814 }
815
816 /***********************************************************************
817  *
818  *           TabbedText functions
819  */
820
821 /***********************************************************************
822  *           TEXT_TabbedTextOut
823  *
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 :-)
827  */
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,
831                                 BOOL fDisplayText )
832 {
833     INT defWidth;
834     SIZE extent;
835     int i, tabPos = x;
836     int start = x;
837
838     extent.cx = 0;
839     extent.cy = 0;
840
841     if (cTabStops == 1)
842     {
843         defWidth = lpTabPos32 ? *lpTabPos32 : *lpTabPos16;
844         cTabStops = 0;
845     }
846     else
847     {
848         TEXTMETRICA tm;
849         GetTextMetricsA( hdc, &tm );
850         defWidth = 8 * tm.tmAveCharWidth;
851     }
852
853     while (count > 0)
854     {
855         for (i = 0; i < count; i++)
856             if (lpstr[i] == '\t') break;
857         GetTextExtentPointA( hdc, lpstr, i, &extent );
858         if (lpTabPos32)
859         {
860             while ((cTabStops > 0) &&
861                    (nTabOrg + *lpTabPos32 <= x + extent.cx))
862             {
863                 lpTabPos32++;
864                 cTabStops--;
865             }
866         }
867         else
868         {
869             while ((cTabStops > 0) &&
870                    (nTabOrg + *lpTabPos16 <= x + extent.cx))
871             {
872                 lpTabPos16++;
873                 cTabStops--;
874             }
875         }
876         if (i == count)
877             tabPos = x + extent.cx;
878         else if (cTabStops > 0)
879             tabPos = nTabOrg + (lpTabPos32 ? *lpTabPos32 : *lpTabPos16);
880         else
881             tabPos = nTabOrg + ((x + extent.cx - nTabOrg) / defWidth + 1) * defWidth;
882         if (fDisplayText)
883         {
884             RECT r;
885             r.left   = x;
886             r.top    = y;
887             r.right  = tabPos;
888             r.bottom = y + extent.cy;
889             ExtTextOutA( hdc, x, y,
890                            GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
891                            &r, lpstr, i, NULL );
892         }
893         x = tabPos;
894         count -= i+1;
895         lpstr += i+1;
896     }
897     return MAKELONG(tabPos - start, extent.cy);
898 }
899
900
901 /***********************************************************************
902  *           TabbedTextOut    (USER.196)
903  */
904 LONG WINAPI TabbedTextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR lpstr,
905                              INT16 count, INT16 cTabStops,
906                              const INT16 *lpTabPos, INT16 nTabOrg )
907 {
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 );
911 }
912
913
914 /***********************************************************************
915  *           TabbedTextOutA    (USER32.@)
916  */
917 LONG WINAPI TabbedTextOutA( HDC hdc, INT x, INT y, LPCSTR lpstr, INT count,
918                             INT cTabStops, const INT *lpTabPos, INT nTabOrg )
919 {
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 );
923 }
924
925
926 /***********************************************************************
927  *           TabbedTextOutW    (USER32.@)
928  */
929 LONG WINAPI TabbedTextOutW( HDC hdc, INT x, INT y, LPCWSTR str, INT count,
930                             INT cTabStops, const INT *lpTabPos, INT nTabOrg )
931 {
932     LONG ret;
933     LPSTR p;
934     INT acount;
935     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
936
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 );
943     return ret;
944 }
945
946
947 /***********************************************************************
948  *           GetTabbedTextExtent    (USER.197)
949  */
950 DWORD WINAPI GetTabbedTextExtent16( HDC16 hdc, LPCSTR lpstr, INT16 count,
951                                     INT16 cTabStops, const INT16 *lpTabPos )
952 {
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 );
956 }
957
958
959 /***********************************************************************
960  *           GetTabbedTextExtentA    (USER32.@)
961  */
962 DWORD WINAPI GetTabbedTextExtentA( HDC hdc, LPCSTR lpstr, INT count,
963                                    INT cTabStops, const INT *lpTabPos )
964 {
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 );
968 }
969
970
971 /***********************************************************************
972  *           GetTabbedTextExtentW    (USER32.@)
973  */
974 DWORD WINAPI GetTabbedTextExtentW( HDC hdc, LPCWSTR lpstr, INT count,
975                                    INT cTabStops, const INT *lpTabPos )
976 {
977     LONG ret;
978     LPSTR p;
979     INT acount;
980     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
981
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 );
988     return ret;
989 }