Fixed other bugs within MMIO implementation. Now, it's possible to
[wine] / objects / text.c
1 /*
2  * text functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  *
6  */
7
8 #include <string.h>
9
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "winbase.h"
14 #include "winuser.h"
15 #include "winerror.h"
16 #include "dc.h"
17 #include "gdi.h"
18 #include "heap.h"
19 #include "debugtools.h"
20 #include "cache.h"
21 #include "winnls.h"
22
23 DEFAULT_DEBUG_CHANNEL(text);
24
25 #define TAB     9
26 #define LF     10
27 #define CR     13
28 #define SPACE  32
29 #define PREFIX 38
30
31 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
32
33 static int tabstop = 8;
34 static int tabwidth;
35 static int spacewidth;
36 static int prefix_offset;
37
38 static const char *TEXT_NextLine( HDC16 hdc, const char *str, int *count,
39                                   char *dest, int *len, int width, WORD format)
40 {
41     /* Return next line of text from a string.
42      * 
43      * hdc - handle to DC.
44      * str - string to parse into lines.
45      * count - length of str.
46      * dest - destination in which to return line.
47      * len - length of resultant line in dest in chars.
48      * width - maximum width of line in pixels.
49      * format - format type passed to DrawText.
50      *
51      * Returns pointer to next char in str after end of the line
52      * or NULL if end of str reached.
53      */
54
55     int i = 0, j = 0, k;
56     int plen = 0;
57     int numspaces;
58     SIZE16 size;
59     int lasttab = 0;
60     int wb_i = 0, wb_j = 0, wb_count = 0;
61
62     while (*count)
63     {
64         switch (str[i])
65         {
66         case CR:
67         case LF:
68             if (!(format & DT_SINGLELINE))
69             {
70                 if ((*count > 1) && (str[i] == CR) && (str[i+1] == LF))
71                 {
72                     (*count)--;
73                     i++;
74                 }
75                 i++;
76                 *len = j;
77                 (*count)--;
78                 return (&str[i]);
79             }
80             dest[j++] = str[i++];
81             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
82                 (format & DT_WORDBREAK))
83             {
84                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
85                     return NULL;
86                 plen += size.cx;
87             }
88             break;
89             
90         case PREFIX:
91             if (!(format & DT_NOPREFIX) && *count > 1)
92                 {
93                 if (str[++i] == PREFIX)
94                     (*count)--;
95                 else {
96                     prefix_offset = j;
97                     break;
98                 }
99             }
100             dest[j++] = str[i++];
101             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
102                 (format & DT_WORDBREAK))
103             {
104                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
105                     return NULL;
106                 plen += size.cx;
107             }
108             break;
109             
110         case TAB:
111             if (format & DT_EXPANDTABS)
112             {
113                 wb_i = ++i;
114                 wb_j = j;
115                 wb_count = *count;
116
117                 if (!GetTextExtentPoint16(hdc, &dest[lasttab], j - lasttab,
118                                                                  &size))
119                     return NULL;
120
121                 numspaces = (tabwidth - size.cx) / spacewidth;
122                 for (k = 0; k < numspaces; k++)
123                     dest[j++] = SPACE;
124                 plen += tabwidth - size.cx;
125                 lasttab = wb_j + numspaces;
126             }
127             else
128             {
129                 dest[j++] = str[i++];
130                 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
131                     (format & DT_WORDBREAK))
132                 {
133                     if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
134                         return NULL;
135                     plen += size.cx;
136                 }
137             }
138             break;
139
140         case SPACE:
141             dest[j++] = str[i++];
142             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
143                 (format & DT_WORDBREAK))
144             {
145                 wb_i = i;
146                 wb_j = j - 1;
147                 wb_count = *count;
148                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
149                     return NULL;
150                 plen += size.cx;
151             }
152             break;
153
154         default:
155             dest[j++] = str[i++];
156             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
157                 (format & DT_WORDBREAK))
158             {
159                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
160                     return NULL;
161                 plen += size.cx;
162             }
163         }
164
165         (*count)--;
166         if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
167         {
168             if (plen > width)
169             {
170                 if (format & DT_WORDBREAK)
171                 {
172                     if (wb_j)
173                     {
174                         *len = wb_j;
175                         *count = wb_count - 1;
176                         return (&str[wb_i]);
177                     }
178                 }
179                 else
180                 {
181                     *len = j;
182                     return (&str[i]);
183                 }
184             }
185         }
186     }
187     
188     *len = j;
189     return NULL;
190 }
191
192
193 /***********************************************************************
194  *           DrawText16    (USER.85)
195  */
196 INT16 WINAPI DrawText16( HDC16 hdc, LPCSTR str, INT16 i_count,
197                          LPRECT16 rect, UINT16 flags )
198 {
199     SIZE16 size;
200     const char *strPtr;
201     static char line[1024];
202     int len, lh, count=i_count;
203     int prefix_x = 0;
204     int prefix_end = 0;
205     TEXTMETRIC16 tm;
206     int x = rect->left, y = rect->top;
207     int width = rect->right - rect->left;
208     int max_width = 0;
209
210     TRACE("%s, %d , [(%d,%d),(%d,%d)]\n",
211           debugstr_an (str, count), count,
212           rect->left, rect->top, rect->right, rect->bottom);
213
214     if (!str) return 0;
215     if (count == -1) count = strlen(str);
216     strPtr = str;
217
218     GetTextMetrics16(hdc, &tm);
219     if (flags & DT_EXTERNALLEADING)
220         lh = tm.tmHeight + tm.tmExternalLeading;
221     else
222         lh = tm.tmHeight;
223
224     if (flags & DT_TABSTOP)
225         tabstop = flags >> 8;
226
227     if (flags & DT_EXPANDTABS)
228     {
229         GetTextExtentPoint16(hdc, " ", 1, &size);
230         spacewidth = size.cx;
231         GetTextExtentPoint16(hdc, "o", 1, &size);
232         tabwidth = size.cx * tabstop;
233     }
234
235     if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
236
237     do
238     {
239         prefix_offset = -1;
240         strPtr = TEXT_NextLine(hdc, strPtr, &count, line, &len, width, flags);
241
242         if (prefix_offset != -1)
243         {
244             GetTextExtentPoint16(hdc, line, prefix_offset, &size);
245             prefix_x = size.cx;
246             GetTextExtentPoint16(hdc, line, prefix_offset + 1, &size);
247             prefix_end = size.cx - 1;
248         }
249
250         if (!GetTextExtentPoint16(hdc, line, len, &size)) return 0;
251         if (flags & DT_CENTER) x = (rect->left + rect->right -
252                                     size.cx) / 2;
253         else if (flags & DT_RIGHT) x = rect->right - size.cx;
254
255         if (flags & DT_SINGLELINE)
256         {
257             if (flags & DT_VCENTER) y = rect->top + 
258                 (rect->bottom - rect->top) / 2 - size.cy / 2;
259             else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
260         }
261         if (!(flags & DT_CALCRECT))
262         {
263             if (!ExtTextOut16(hdc, x, y, (flags & DT_NOCLIP) ? 0 : ETO_CLIPPED,
264                               rect, line, len, NULL )) return 0;
265             if (prefix_offset != -1)
266             {
267                 HPEN hpen = CreatePen( PS_SOLID, 1, GetTextColor(hdc) );
268                 HPEN oldPen = SelectObject( hdc, hpen );
269                 MoveTo16(hdc, x + prefix_x, y + tm.tmAscent + 1 );
270                 LineTo(hdc, x + prefix_end + 1, y + tm.tmAscent + 1 );
271                 SelectObject( hdc, oldPen );
272                 DeleteObject( hpen );
273             }
274         }
275         else if (size.cx > max_width)
276             max_width = size.cx;
277
278         y += lh;
279         if (strPtr)
280         {
281             if (!(flags & DT_NOCLIP))
282             {
283                 if (y > rect->bottom - lh)
284                     break;
285             }
286         }
287     }
288     while (strPtr);
289     if (flags & DT_CALCRECT)
290     {
291         rect->right = rect->left + max_width;
292         rect->bottom = y;
293     }
294     return y - rect->top;
295 }
296
297
298 /***********************************************************************
299  *           DrawTextA    (USER32.164)
300  */
301 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count,
302                           LPRECT rect, UINT flags )
303 {
304     RECT16 rect16;
305     INT16 ret;
306
307     if (!rect)
308         return DrawText16( (HDC16)hdc, str, (INT16)count, NULL, (UINT16)flags);
309     CONV_RECT32TO16( rect, &rect16 );
310     ret = DrawText16( (HDC16)hdc, str, (INT16)count, &rect16, (UINT16)flags );
311     CONV_RECT16TO32( &rect16, rect );
312     return ret;
313 }
314
315
316 /***********************************************************************
317  *           DrawTextW    (USER32.167)
318  */
319 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count,
320                           LPRECT rect, UINT flags )
321 {
322     LPSTR p;
323     INT acount;
324     INT ret;
325     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
326         
327     acount = WideCharToMultiByte(codepage,0,str,count,NULL,0,NULL,NULL);
328     p = HeapAlloc( GetProcessHeap(), 0, acount );
329     acount = WideCharToMultiByte(codepage,0,str,count,p,acount,NULL,NULL);
330     ret = DrawTextA( hdc, p, acount, rect, flags );
331
332     HeapFree( GetProcessHeap(), 0, p );
333     return ret;
334 }
335
336 /***********************************************************************
337  *           DrawTextExA    (USER32.165)
338  */
339 INT WINAPI DrawTextExA( HDC hdc, LPCSTR str, INT count,
340                      LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
341 {
342     TRACE("(%d,'%s',%d,%p,0x%08x,%p)\n",hdc,str,count,rect,flags,dtp);
343     if(dtp) {
344         FIXME("Ignores params:%d,%d,%d,%d,%d\n",dtp->cbSize,
345                    dtp->iTabLength,dtp->iLeftMargin,dtp->iRightMargin,
346                    dtp->uiLengthDrawn);
347     }
348     return DrawTextA(hdc,str,count,rect,flags);
349 }
350
351 /***********************************************************************
352  *           DrawTextExW    (USER32.166)
353  */
354 INT WINAPI DrawTextExW( HDC hdc, LPCWSTR str, INT count,
355                      LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
356 {
357     TRACE("(%d,%p,%d,%p,0x%08x,%p)\n",hdc,str,count,rect,flags,dtp);
358     FIXME("ignores extended functionality\n");
359     return DrawTextW(hdc,str,count,rect,flags);
360 }
361
362 /***********************************************************************
363  *           ExtTextOut16    (GDI.351)
364  */
365 BOOL16 WINAPI ExtTextOut16( HDC16 hdc, INT16 x, INT16 y, UINT16 flags,
366                             const RECT16 *lprect, LPCSTR str, UINT16 count,
367                             const INT16 *lpDx )
368 {
369     BOOL        ret;
370     int         i;
371     RECT        rect32;
372     LPINT       lpdx32 = NULL;
373
374     if (lpDx) lpdx32 = (LPINT)HEAP_xalloc( GetProcessHeap(), 0,
375                                              sizeof(INT)*count );
376     if (lprect) CONV_RECT16TO32(lprect,&rect32);
377     if (lpdx32) for (i=count;i--;) lpdx32[i]=lpDx[i];
378     ret = ExtTextOutA(hdc,x,y,flags,lprect?&rect32:NULL,str,count,lpdx32);
379     if (lpdx32) HeapFree( GetProcessHeap(), 0, lpdx32 );
380     return ret;
381
382
383 }
384
385
386 /***********************************************************************
387  *           ExtTextOutA    (GDI32.98)
388  */
389 BOOL WINAPI ExtTextOutA( HDC hdc, INT x, INT y, UINT flags,
390                              const RECT *lprect, LPCSTR str, UINT count,
391                              const INT *lpDx )
392 {
393     LPWSTR p;
394     INT ret;
395     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
396     UINT wlen;
397     LPINT lpDxW = NULL;
398
399     wlen = MultiByteToWideChar(codepage,0,str,count,NULL,0);
400     if(lpDx){
401         int i, j;
402         i = 0; j = 0;
403
404         lpDxW = (LPINT)HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(INT));
405         while(i < count){
406             if(IsDBCSLeadByteEx(codepage, str[i])){
407                 lpDxW[j++] = lpDx[i] + lpDx[i+1];
408                 i = i + 2;
409             }
410             else{
411                 lpDxW[j++] = lpDx[i];
412                 i = i + 1;
413             }
414         }
415         lpDx = lpDxW;
416     }
417     p = HeapAlloc( GetProcessHeap(), 0, wlen * sizeof(WCHAR) );
418     wlen = MultiByteToWideChar(codepage,0,str,count,p,wlen);
419     ret = ExtTextOutW( hdc, x, y, flags, lprect, p, wlen, lpDxW );
420     if (lpDxW) HeapFree( GetProcessHeap(), 0, lpDxW );
421     HeapFree( GetProcessHeap(), 0, p );
422     return ret;
423 }
424
425
426 /***********************************************************************
427  *           ExtTextOutW    (GDI32.99)
428  */
429 BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags,
430                              const RECT *lprect, LPCWSTR str, UINT count,
431                              const INT *lpDx )
432 {
433     DC * dc = DC_GetDCPtr( hdc );
434     return dc && dc->funcs->pExtTextOut && 
435         dc->funcs->pExtTextOut(dc,x,y,flags,lprect,str,count,lpDx);
436 }
437
438
439 /***********************************************************************
440  *           TextOut16    (GDI.33)
441  */
442 BOOL16 WINAPI TextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR str, INT16 count )
443 {
444     return ExtTextOut16( hdc, x, y, 0, NULL, str, count, NULL );
445 }
446
447
448 /***********************************************************************
449  *           TextOutA    (GDI32.355)
450  */
451 BOOL WINAPI TextOutA( HDC hdc, INT x, INT y, LPCSTR str, INT count )
452 {
453     return ExtTextOutA( hdc, x, y, 0, NULL, str, count, NULL );
454 }
455
456
457 /***********************************************************************
458  *           TextOutW    (GDI32.356)
459  */
460 BOOL WINAPI TextOutW(HDC hdc, INT x, INT y, LPCWSTR str, INT count)
461 {
462     return ExtTextOutW( hdc, x, y, 0, NULL, str, count, NULL );
463 }
464
465
466 /***********************************************************************
467  *           TEXT_GrayString
468  *
469  * FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
470  * heap and we can guarantee that the handles fit in an INT16. We have to
471  * rethink the strategy once the migration to NT handles is complete.
472  * We are going to get a lot of code-duplication once this migration is
473  * completed...
474  * 
475  */
476 static BOOL TEXT_GrayString(HDC hdc, HBRUSH hb, 
477                               GRAYSTRINGPROC fn, LPARAM lp, INT len,
478                               INT x, INT y, INT cx, INT cy, 
479                               BOOL unicode, BOOL _32bit)
480 {
481     HBITMAP hbm, hbmsave;
482     HBRUSH hbsave;
483     HFONT hfsave;
484     HDC memdc = CreateCompatibleDC(hdc);
485     int slen = len;
486     BOOL retval = TRUE;
487     COLORREF fg, bg;
488
489     if(!hdc) return FALSE;
490     
491     if(len == 0)
492     {
493         if(unicode)
494             slen = lstrlenW((LPCWSTR)lp);
495         else if(_32bit)
496             slen = lstrlenA((LPCSTR)lp);
497         else
498             slen = lstrlenA((LPCSTR)PTR_SEG_TO_LIN(lp));
499     }
500
501     if((cx == 0 || cy == 0) && slen != -1)
502     {
503         SIZE s;
504         if(unicode)
505             GetTextExtentPoint32W(hdc, (LPCWSTR)lp, slen, &s);
506         else if(_32bit)
507             GetTextExtentPoint32A(hdc, (LPCSTR)lp, slen, &s);
508         else
509             GetTextExtentPoint32A(hdc, (LPCSTR)PTR_SEG_TO_LIN(lp), slen, &s);
510         if(cx == 0) cx = s.cx;
511         if(cy == 0) cy = s.cy;
512     }
513
514     hbm = CreateBitmap(cx, cy, 1, 1, NULL);
515     hbmsave = (HBITMAP)SelectObject(memdc, hbm);
516     hbsave = SelectObject( memdc, GetStockObject(BLACK_BRUSH) );
517     PatBlt( memdc, 0, 0, cx, cy, PATCOPY );
518     SelectObject( memdc, hbsave );
519     SetTextColor(memdc, RGB(255, 255, 255));
520     SetBkColor(memdc, RGB(0, 0, 0));
521     hfsave = (HFONT)SelectObject(memdc, GetCurrentObject(hdc, OBJ_FONT));
522             
523     if(fn)
524         if(_32bit)
525             retval = fn(memdc, lp, slen);
526         else
527             retval = (BOOL)((BOOL16)((GRAYSTRINGPROC16)fn)((HDC16)memdc, lp, (INT16)slen));
528     else
529         if(unicode)
530             TextOutW(memdc, 0, 0, (LPCWSTR)lp, slen);
531         else if(_32bit)
532             TextOutA(memdc, 0, 0, (LPCSTR)lp, slen);
533         else
534             TextOutA(memdc, 0, 0, (LPCSTR)PTR_SEG_TO_LIN(lp), slen);
535
536     SelectObject(memdc, hfsave);
537
538 /*
539  * Windows doc says that the bitmap isn't grayed when len == -1 and
540  * the callback function returns FALSE. However, testing this on
541  * win95 showed otherwise...
542 */
543 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
544     if(retval || len != -1)
545 #endif
546     {
547         hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
548         PatBlt(memdc, 0, 0, cx, cy, 0x000A0329);
549         SelectObject(memdc, hbsave);
550     }
551
552     if(hb) hbsave = (HBRUSH)SelectObject(hdc, hb);
553     fg = SetTextColor(hdc, RGB(0, 0, 0));
554     bg = SetBkColor(hdc, RGB(255, 255, 255));
555     BitBlt(hdc, x, y, cx, cy, memdc, 0, 0, 0x00E20746);
556     SetTextColor(hdc, fg);
557     SetBkColor(hdc, bg);
558     if(hb) SelectObject(hdc, hbsave);
559
560     SelectObject(memdc, hbmsave);
561     DeleteObject(hbm);
562     DeleteDC(memdc);
563     return retval;
564 }
565
566
567 /***********************************************************************
568  *           GrayString16   (USER.185)
569  */
570 BOOL16 WINAPI GrayString16( HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc,
571                             LPARAM lParam, INT16 cch, INT16 x, INT16 y,
572                             INT16 cx, INT16 cy )
573 {
574     return TEXT_GrayString(hdc, hbr, (GRAYSTRINGPROC)gsprc, lParam, cch, x, y, cx, cy, FALSE, FALSE);
575 }
576
577
578 /***********************************************************************
579  *           GrayStringA   (USER32.315)
580  */
581 BOOL WINAPI GrayStringA( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
582                              LPARAM lParam, INT cch, INT x, INT y,
583                              INT cx, INT cy )
584 {
585     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy, FALSE, TRUE);
586 }
587
588
589 /***********************************************************************
590  *           GrayStringW   (USER32.316)
591  */
592 BOOL WINAPI GrayStringW( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
593                              LPARAM lParam, INT cch, INT x, INT y,
594                              INT cx, INT cy )
595 {
596     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy, TRUE, TRUE);
597 }
598
599
600 /***********************************************************************
601  *           TEXT_TabbedTextOut
602  *
603  * Helper function for TabbedTextOut() and GetTabbedTextExtent().
604  * Note: this doesn't work too well for text-alignment modes other
605  *       than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
606  */
607 LONG TEXT_TabbedTextOut( HDC hdc, INT x, INT y, LPCSTR lpstr,
608                          INT count, INT cTabStops, const INT16 *lpTabPos16,
609                          const INT *lpTabPos32, INT nTabOrg,
610                          BOOL fDisplayText )
611 {
612     INT defWidth;
613     DWORD extent = 0;
614     int i, tabPos = x;
615     int start = x;
616
617     if (cTabStops == 1)
618     {
619         defWidth = lpTabPos32 ? *lpTabPos32 : *lpTabPos16;
620         cTabStops = 0;
621     }
622     else
623     {
624         TEXTMETRIC16 tm;
625         GetTextMetrics16( hdc, &tm );
626         defWidth = 8 * tm.tmAveCharWidth;
627     }
628     
629     while (count > 0)
630     {
631         for (i = 0; i < count; i++)
632             if (lpstr[i] == '\t') break;
633         extent = GetTextExtent16( hdc, lpstr, i );
634         if (lpTabPos32)
635         {
636             while ((cTabStops > 0) &&
637                    (nTabOrg + *lpTabPos32 <= x + LOWORD(extent)))
638             {
639                 lpTabPos32++;
640                 cTabStops--;
641             }
642         }
643         else
644         {
645             while ((cTabStops > 0) &&
646                    (nTabOrg + *lpTabPos16 <= x + LOWORD(extent)))
647             {
648                 lpTabPos16++;
649                 cTabStops--;
650             }
651         }
652         if (i == count)
653             tabPos = x + LOWORD(extent);
654         else if (cTabStops > 0)
655             tabPos = nTabOrg + (lpTabPos32 ? *lpTabPos32 : *lpTabPos16);
656         else
657             tabPos = nTabOrg + ((x + LOWORD(extent) - nTabOrg) / defWidth + 1) * defWidth;
658         if (fDisplayText)
659         {
660             RECT r;
661             r.left   = x;
662             r.top    = y;
663             r.right  = tabPos;
664             r.bottom = y + HIWORD(extent);
665             ExtTextOutA( hdc, x, y,
666                            GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
667                            &r, lpstr, i, NULL );
668         }
669         x = tabPos;
670         count -= i+1;
671         lpstr += i+1;
672     }
673     return MAKELONG(tabPos - start, HIWORD(extent));
674 }
675
676
677 /***********************************************************************
678  *           TabbedTextOut16    (USER.196)
679  */
680 LONG WINAPI TabbedTextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR lpstr,
681                              INT16 count, INT16 cTabStops,
682                              const INT16 *lpTabPos, INT16 nTabOrg )
683 {
684     TRACE("%04x %d,%d '%.*s' %d\n",
685                   hdc, x, y, count, lpstr, count );
686     return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
687                                lpTabPos, NULL, nTabOrg, TRUE );
688 }
689
690
691 /***********************************************************************
692  *           TabbedTextOutA    (USER32.542)
693  */
694 LONG WINAPI TabbedTextOutA( HDC hdc, INT x, INT y, LPCSTR lpstr,
695                               INT count, INT cTabStops,
696                               const INT *lpTabPos, INT nTabOrg )
697 {
698     TRACE("%04x %d,%d '%.*s' %d\n",
699                   hdc, x, y, count, lpstr, count );
700     return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
701                                NULL, lpTabPos, nTabOrg, TRUE );
702 }
703
704
705 /***********************************************************************
706  *           TabbedTextOutW    (USER32.543)
707  */
708 LONG WINAPI TabbedTextOutW( HDC hdc, INT x, INT y, LPCWSTR str,
709                               INT count, INT cTabStops,
710                               const INT *lpTabPos, INT nTabOrg )
711 {
712     LONG ret;
713     LPSTR p;
714     INT acount;
715     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
716
717     acount = WideCharToMultiByte(codepage,0,str,count,NULL,0,NULL,NULL);
718     p = HEAP_xalloc( GetProcessHeap(), 0, acount ); 
719     acount = WideCharToMultiByte(codepage,0,str,count,p,acount,NULL,NULL);
720     ret = TabbedTextOutA( hdc, x, y, p, acount, cTabStops,
721                             lpTabPos, nTabOrg );
722     HeapFree( GetProcessHeap(), 0, p );
723     return ret;
724 }
725
726
727 /***********************************************************************
728  *           GetTabbedTextExtent16    (USER.197)
729  */
730 DWORD WINAPI GetTabbedTextExtent16( HDC16 hdc, LPCSTR lpstr, INT16 count, 
731                                     INT16 cTabStops, const INT16 *lpTabPos )
732 {
733     TRACE("%04x '%.*s' %d\n",
734                   hdc, count, lpstr, count );
735     return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
736                                lpTabPos, NULL, 0, FALSE );
737 }
738
739
740 /***********************************************************************
741  *           GetTabbedTextExtentA    (USER32.293)
742  */
743 DWORD WINAPI GetTabbedTextExtentA( HDC hdc, LPCSTR lpstr, INT count, 
744                                      INT cTabStops, const INT *lpTabPos )
745 {
746     TRACE("%04x '%.*s' %d\n",
747                   hdc, count, lpstr, count );
748     return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
749                                NULL, lpTabPos, 0, FALSE );
750 }
751
752
753 /***********************************************************************
754  *           GetTabbedTextExtentW    (USER32.294)
755  */
756 DWORD WINAPI GetTabbedTextExtentW( HDC hdc, LPCWSTR lpstr, INT count, 
757                                      INT cTabStops, const INT *lpTabPos )
758 {
759     LONG ret;
760     LPSTR p;
761     INT acount;
762     UINT codepage = CP_ACP; /* FIXME: get codepage of font charset */
763
764     acount = WideCharToMultiByte(codepage,0,lpstr,count,NULL,0,NULL,NULL);
765     p = HEAP_xalloc( GetProcessHeap(), 0, acount );
766     acount = WideCharToMultiByte(codepage,0,lpstr,count,p,acount,NULL,NULL);
767     ret = GetTabbedTextExtentA( hdc, p, acount, cTabStops, lpTabPos );
768     HeapFree( GetProcessHeap(), 0, p );
769     return ret;
770 }
771
772 /***********************************************************************
773  * GetTextCharset [GDI32.226]  Gets character set for font in DC
774  *
775  * NOTES
776  *    Should it return a UINT32 instead of an INT32?
777  *    => YES, as GetTextCharsetInfo returns UINT32
778  *
779  * RETURNS
780  *    Success: Character set identifier
781  *    Failure: DEFAULT_CHARSET
782  */
783 UINT WINAPI GetTextCharset(
784     HDC hdc) /* [in] Handle to device context */
785 {
786     /* MSDN docs say this is equivalent */
787     return GetTextCharsetInfo(hdc, NULL, 0);
788 }
789
790 /***********************************************************************
791  * GetTextCharset16 [GDI.612]
792  */
793 UINT16 WINAPI GetTextCharset16(HDC16 hdc)
794 {
795     return (UINT16)GetTextCharset(hdc);
796 }
797
798 /***********************************************************************
799  * GetTextCharsetInfo [GDI32.381]  Gets character set for font
800  *
801  * NOTES
802  *    Should csi be an LPFONTSIGNATURE instead of an LPCHARSETINFO?
803  *    Should it return a UINT32 instead of an INT32?
804  *    => YES and YES, from win32.hlp from Borland
805  *
806  * RETURNS
807  *    Success: Character set identifier
808  *    Failure: DEFAULT_CHARSET
809  */
810 UINT WINAPI GetTextCharsetInfo(
811     HDC hdc,          /* [in]  Handle to device context */
812     LPFONTSIGNATURE fs, /* [out] Pointer to struct to receive data */
813     DWORD flags)        /* [in]  Reserved - must be 0 */
814 {
815     HGDIOBJ hFont;
816     UINT charSet = DEFAULT_CHARSET;
817     LOGFONTW lf;
818     CHARSETINFO csinfo;
819
820     hFont = GetCurrentObject(hdc, OBJ_FONT);
821     if (hFont == 0)
822         return(DEFAULT_CHARSET);
823     if ( GetObjectW(hFont, sizeof(LOGFONTW), &lf) != 0 )
824         charSet = lf.lfCharSet;
825
826     if (fs != NULL) {
827       if (!TranslateCharsetInfo((LPDWORD)charSet, &csinfo, TCI_SRCCHARSET))
828            return  (DEFAULT_CHARSET);
829       memcpy(fs, &csinfo.fs, sizeof(FONTSIGNATURE));
830     }
831     return charSet;
832 }
833
834 /***********************************************************************
835  * PolyTextOutA [GDI.402]  Draw several Strings
836  */
837 BOOL WINAPI PolyTextOutA (
838                           HDC hdc,               /* Handle to device context */                   
839                           PPOLYTEXTA pptxt,      /* array of strings */
840                           INT cStrings           /* Number of strings in array */
841                           )
842 {
843   FIXME("stub!\n");
844   SetLastError ( ERROR_CALL_NOT_IMPLEMENTED );
845   return 0;
846 }
847
848
849
850 /***********************************************************************
851  * PolyTextOutW [GDI.403] Draw several Strings
852  */
853 BOOL WINAPI PolyTextOutW ( 
854                           HDC hdc,               /* Handle to device context */                   
855                           PPOLYTEXTW pptxt,      /* array of strings */
856                           INT cStrings           /* Number of strings in array */
857                           )
858 {
859   FIXME("stub!\n");
860   SetLastError ( ERROR_CALL_NOT_IMPLEMENTED );
861   return 0;
862 }