4 * Copyright 1993, 1994 Alexandre Julliard
12 #include "wine/winuser16.h"
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(text
);
28 #define ELLIPSIS "..."
29 #define FORWARD_SLASH '/'
30 #define BACK_SLASH '\\'
32 #define SWAP_INT(a,b) { int t = a; a = b; b = t; }
34 static int tabstop
= 8;
36 static int spacewidth
;
37 static int prefix_offset
;
39 static const char *TEXT_NextLine( HDC hdc
, const char *str
, int *count
,
40 char *dest
, int *len
, int width
, WORD format
)
42 /* Return next line of text from a string.
45 * str - string to parse into lines.
46 * count - length of str.
47 * dest - destination in which to return line.
48 * len - length of resultant line in dest in chars.
49 * width - maximum width of line in pixels.
50 * format - format type passed to DrawText.
52 * Returns pointer to next char in str after end of the line
53 * or NULL if end of str reached.
57 * GetTextExtentPoint is used to get the width of each character,
58 * rather than GetCharABCWidth... So the whitespace between
59 * characters is ignored, and the reported len is too great.
67 int wb_i
= 0, wb_j
= 0, wb_count
= 0;
75 if (!(format
& DT_SINGLELINE
))
77 if ((*count
> 1) && (str
[i
] == CR
) && (str
[i
+1] == LF
))
88 if (!(format
& DT_NOCLIP
) || !(format
& DT_NOPREFIX
) ||
89 (format
& DT_WORDBREAK
))
91 if (!GetTextExtentPointA(hdc
, &dest
[j
-1], 1, &size
))
98 if (!(format
& DT_NOPREFIX
) && *count
> 1)
100 if (str
[++i
] == PREFIX
)
107 dest
[j
++] = str
[i
++];
108 if (!(format
& DT_NOCLIP
) || !(format
& DT_NOPREFIX
) ||
109 (format
& DT_WORDBREAK
))
111 if (!GetTextExtentPointA(hdc
, &dest
[j
-1], 1, &size
))
118 if (format
& DT_EXPANDTABS
)
124 if (!GetTextExtentPointA(hdc
, &dest
[lasttab
], j
- lasttab
, &size
))
127 numspaces
= (tabwidth
- size
.cx
) / spacewidth
;
128 for (k
= 0; k
< numspaces
; k
++)
130 plen
+= tabwidth
- size
.cx
;
131 lasttab
= wb_j
+ numspaces
;
135 dest
[j
++] = str
[i
++];
136 if (!(format
& DT_NOCLIP
) || !(format
& DT_NOPREFIX
) ||
137 (format
& DT_WORDBREAK
))
139 if (!GetTextExtentPointA(hdc
, &dest
[j
-1], 1, &size
))
147 dest
[j
++] = str
[i
++];
148 if (!(format
& DT_NOCLIP
) || !(format
& DT_NOPREFIX
) ||
149 (format
& DT_WORDBREAK
))
154 if (!GetTextExtentPointA(hdc
, &dest
[j
-1], 1, &size
))
161 dest
[j
++] = str
[i
++];
162 if (!(format
& DT_NOCLIP
) || !(format
& DT_NOPREFIX
) ||
163 (format
& DT_WORDBREAK
))
165 if (!GetTextExtentPointA(hdc
, &dest
[j
-1], 1, &size
))
172 if (!(format
& DT_NOCLIP
) || (format
& DT_WORDBREAK
))
176 if (format
& DT_WORDBREAK
)
181 *count
= wb_count
- 1;
199 /***********************************************************************
200 * DrawText16 (USER.85)
202 INT16 WINAPI
DrawText16( HDC16 hdc
, LPCSTR str
, INT16 count
, LPRECT16 rect
, UINT16 flags
)
209 CONV_RECT16TO32( rect
, &rect32
);
210 ret
= DrawTextA( hdc
, str
, count
, &rect32
, flags
);
211 CONV_RECT32TO16( &rect32
, rect
);
213 else ret
= DrawTextA( hdc
, str
, count
, NULL
, flags
);
218 /***********************************************************************
219 * DrawTextA (USER32.164)
221 INT WINAPI
DrawTextA( HDC hdc
, LPCSTR str
, INT i_count
, LPRECT rect
, UINT flags
)
225 static char line
[1024];
226 int len
, lh
, count
=i_count
;
230 int x
= rect
->left
, y
= rect
->top
;
231 int width
= rect
->right
- rect
->left
;
234 TRACE("%s, %d , [(%d,%d),(%d,%d)]\n",
235 debugstr_an (str
, count
), count
,
236 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
239 if (count
== -1) count
= strlen(str
);
240 if (count
== 0) return 0;
243 GetTextMetricsA(hdc
, &tm
);
244 if (flags
& DT_EXTERNALLEADING
)
245 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
249 if (flags
& DT_TABSTOP
)
250 tabstop
= flags
>> 8;
252 if (flags
& DT_EXPANDTABS
)
254 GetTextExtentPointA(hdc
, " ", 1, &size
);
255 spacewidth
= size
.cx
;
256 GetTextExtentPointA(hdc
, "o", 1, &size
);
257 tabwidth
= size
.cx
* tabstop
;
260 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
265 strPtr
= TEXT_NextLine(hdc
, strPtr
, &count
, line
, &len
, width
, flags
);
267 if (prefix_offset
!= -1)
269 GetTextExtentPointA(hdc
, line
, prefix_offset
, &size
);
271 GetTextExtentPointA(hdc
, line
, prefix_offset
+ 1, &size
);
272 prefix_end
= size
.cx
- 1;
275 if (!GetTextExtentPointA(hdc
, line
, len
, &size
)) return 0;
276 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
278 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
280 if (flags
& DT_SINGLELINE
)
282 if (flags
& DT_VCENTER
) y
= rect
->top
+
283 (rect
->bottom
- rect
->top
) / 2 - size
.cy
/ 2;
284 else if (flags
& DT_BOTTOM
) y
= rect
->bottom
- size
.cy
;
286 if (flags
& (DT_PATH_ELLIPSIS
| DT_END_ELLIPSIS
| DT_WORD_ELLIPSIS
))
288 char swapStr
[sizeof(line
)];
289 char* fnameDelim
= NULL
;
290 int totalLen
= i_count
>= 0 ? i_count
: strlen(str
);
294 int fnameLen
= totalLen
;
296 /* allow room for '...' */
297 count
= min(totalLen
+3, sizeof(line
)-3);
299 if (flags
& DT_WORD_ELLIPSIS
)
300 flags
|= DT_WORDBREAK
;
302 if (flags
& DT_PATH_ELLIPSIS
)
304 char* lastBkSlash
= NULL
;
305 char* lastFwdSlash
= NULL
;
306 strncpy(line
, str
, totalLen
); line
[totalLen
] = '\0';
307 lastBkSlash
= strrchr(line
, BACK_SLASH
);
308 lastFwdSlash
= strrchr(line
, FORWARD_SLASH
);
309 fnameDelim
= lastFwdSlash
? lastFwdSlash
: lastBkSlash
;
310 if (lastBkSlash
&& lastFwdSlash
) /* which is last? */
311 if (lastBkSlash
> lastFwdSlash
)
312 fnameDelim
= lastBkSlash
;
315 fnameLen
= &line
[totalLen
] - fnameDelim
;
317 fnameDelim
= (char*)str
;
319 strcpy(swapStr
, ELLIPSIS
);
320 strncat(swapStr
, fnameDelim
, fnameLen
);
321 swapStr
[fnameLen
+3] = '\0';
322 strncat(swapStr
, str
, totalLen
- fnameLen
);
323 swapStr
[totalLen
+3] = '\0';
325 else /* DT_END_ELLIPSIS | DT_WORD_ELLIPSIS */
327 strcpy(swapStr
, ELLIPSIS
);
328 strncat(swapStr
, str
, totalLen
);
331 TEXT_NextLine(hdc
, swapStr
, &count
, line
, &len
, width
, flags
);
333 /* if only the ELLIPSIS will fit, just let it be clipped */
335 GetTextExtentPointA(hdc
, line
, len
, &size
);
338 * NextLine uses GetTextExtentPoint for each character,
339 * rather than GetCharABCWidth... So the whitespace between
340 * characters is ignored in the width measurement, and the
341 * reported len is too great. To compensate, we must get
342 * the width of the entire line and adjust len accordingly.
344 while ((size
.cx
> width
) && (len
> 3))
347 GetTextExtentPointA(hdc
, line
, len
, &size
);
350 if (fnameLen
< len
-3) /* some of the path will fit */
352 /* put the ELLIPSIS between the path and filename */
353 strncpy(swapStr
, &line
[fnameLen
+3], len
-3-fnameLen
);
354 swapStr
[len
-3-fnameLen
] = '\0';
355 strcat(swapStr
, ELLIPSIS
);
356 strncat(swapStr
, &line
[3], fnameLen
);
360 /* move the ELLIPSIS to the end */
361 strncpy(swapStr
, &line
[3], len
-3);
362 swapStr
[len
-3] = '\0';
363 strcat(swapStr
, ELLIPSIS
);
366 strncpy(line
, swapStr
, len
);
372 if (!(flags
& DT_CALCRECT
))
374 if (!ExtTextOutA(hdc
, x
, y
, (flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
,
375 rect
, line
, len
, NULL
)) return 0;
376 if (prefix_offset
!= -1)
378 HPEN hpen
= CreatePen( PS_SOLID
, 1, GetTextColor(hdc
) );
379 HPEN oldPen
= SelectObject( hdc
, hpen
);
380 MoveToEx(hdc
, x
+ prefix_x
, y
+ tm
.tmAscent
+ 1, NULL
);
381 LineTo(hdc
, x
+ prefix_end
+ 1, y
+ tm
.tmAscent
+ 1 );
382 SelectObject( hdc
, oldPen
);
383 DeleteObject( hpen
);
386 else if (size
.cx
> max_width
)
392 if (!(flags
& DT_NOCLIP
))
394 if (y
> rect
->bottom
- lh
)
400 if (flags
& DT_CALCRECT
)
402 rect
->right
= rect
->left
+ max_width
;
405 return y
- rect
->top
;
409 /***********************************************************************
410 * DrawTextW (USER32.167)
412 INT WINAPI
DrawTextW( HDC hdc
, LPCWSTR str
, INT count
,
413 LPRECT rect
, UINT flags
)
418 UINT codepage
= CP_ACP
; /* FIXME: get codepage of font charset */
420 acount
= WideCharToMultiByte(codepage
,0,str
,count
,NULL
,0,NULL
,NULL
);
421 p
= HeapAlloc( GetProcessHeap(), 0, acount
);
422 acount
= WideCharToMultiByte(codepage
,0,str
,count
,p
,acount
,NULL
,NULL
);
423 if (count
== -1) acount
= -1;
424 ret
= DrawTextA( hdc
, p
, acount
, rect
, flags
);
426 HeapFree( GetProcessHeap(), 0, p
);
430 /***********************************************************************
431 * DrawTextExA (USER32.165)
433 INT WINAPI
DrawTextExA( HDC hdc
, LPCSTR str
, INT count
,
434 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
436 TRACE("(%d,'%s',%d,%p,0x%08x,%p)\n",hdc
,str
,count
,rect
,flags
,dtp
);
438 FIXME("Ignores params:%d,%d,%d,%d\n",dtp
->cbSize
,
439 dtp
->iTabLength
,dtp
->iLeftMargin
,dtp
->iRightMargin
);
441 return DrawTextA(hdc
,str
,count
,rect
,flags
);
444 /***********************************************************************
445 * DrawTextExW (USER32.166)
447 INT WINAPI
DrawTextExW( HDC hdc
, LPCWSTR str
, INT count
,
448 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
450 TRACE("(%d,%p,%d,%p,0x%08x,%p)\n",hdc
,str
,count
,rect
,flags
,dtp
);
451 FIXME("ignores extended functionality\n");
452 return DrawTextW(hdc
,str
,count
,rect
,flags
);
454 /***********************************************************************
457 * FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
458 * heap and we can guarantee that the handles fit in an INT16. We have to
459 * rethink the strategy once the migration to NT handles is complete.
460 * We are going to get a lot of code-duplication once this migration is
464 static BOOL
TEXT_GrayString(HDC hdc
, HBRUSH hb
, GRAYSTRINGPROC fn
, LPARAM lp
, INT len
,
465 INT x
, INT y
, INT cx
, INT cy
, BOOL unicode
, BOOL _32bit
)
467 HBITMAP hbm
, hbmsave
;
470 HDC memdc
= CreateCompatibleDC(hdc
);
475 if(!hdc
) return FALSE
;
480 slen
= lstrlenW((LPCWSTR
)lp
);
482 slen
= strlen((LPCSTR
)lp
);
484 slen
= strlen((LPCSTR
)PTR_SEG_TO_LIN(lp
));
487 if((cx
== 0 || cy
== 0) && slen
!= -1)
491 GetTextExtentPoint32W(hdc
, (LPCWSTR
)lp
, slen
, &s
);
493 GetTextExtentPoint32A(hdc
, (LPCSTR
)lp
, slen
, &s
);
495 GetTextExtentPoint32A(hdc
, (LPCSTR
)PTR_SEG_TO_LIN(lp
), slen
, &s
);
496 if(cx
== 0) cx
= s
.cx
;
497 if(cy
== 0) cy
= s
.cy
;
500 hbm
= CreateBitmap(cx
, cy
, 1, 1, NULL
);
501 hbmsave
= (HBITMAP
)SelectObject(memdc
, hbm
);
502 hbsave
= SelectObject( memdc
, GetStockObject(BLACK_BRUSH
) );
503 PatBlt( memdc
, 0, 0, cx
, cy
, PATCOPY
);
504 SelectObject( memdc
, hbsave
);
505 SetTextColor(memdc
, RGB(255, 255, 255));
506 SetBkColor(memdc
, RGB(0, 0, 0));
507 hfsave
= (HFONT
)SelectObject(memdc
, GetCurrentObject(hdc
, OBJ_FONT
));
511 retval
= fn(memdc
, lp
, slen
);
513 retval
= (BOOL
)((BOOL16
)((GRAYSTRINGPROC16
)fn
)((HDC16
)memdc
, lp
, (INT16
)slen
));
516 TextOutW(memdc
, 0, 0, (LPCWSTR
)lp
, slen
);
518 TextOutA(memdc
, 0, 0, (LPCSTR
)lp
, slen
);
520 TextOutA(memdc
, 0, 0, (LPCSTR
)PTR_SEG_TO_LIN(lp
), slen
);
522 SelectObject(memdc
, hfsave
);
525 * Windows doc says that the bitmap isn't grayed when len == -1 and
526 * the callback function returns FALSE. However, testing this on
527 * win95 showed otherwise...
529 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
530 if(retval
|| len
!= -1)
533 hbsave
= (HBRUSH
)SelectObject(memdc
, CACHE_GetPattern55AABrush());
534 PatBlt(memdc
, 0, 0, cx
, cy
, 0x000A0329);
535 SelectObject(memdc
, hbsave
);
538 if(hb
) hbsave
= (HBRUSH
)SelectObject(hdc
, hb
);
539 fg
= SetTextColor(hdc
, RGB(0, 0, 0));
540 bg
= SetBkColor(hdc
, RGB(255, 255, 255));
541 BitBlt(hdc
, x
, y
, cx
, cy
, memdc
, 0, 0, 0x00E20746);
542 SetTextColor(hdc
, fg
);
544 if(hb
) SelectObject(hdc
, hbsave
);
546 SelectObject(memdc
, hbmsave
);
553 /***********************************************************************
554 * GrayString16 (USER.185)
556 BOOL16 WINAPI
GrayString16( HDC16 hdc
, HBRUSH16 hbr
, GRAYSTRINGPROC16 gsprc
,
557 LPARAM lParam
, INT16 cch
, INT16 x
, INT16 y
,
560 return TEXT_GrayString(hdc
, hbr
, (GRAYSTRINGPROC
)gsprc
, lParam
, cch
, x
, y
, cx
, cy
, FALSE
, FALSE
);
564 /***********************************************************************
565 * GrayStringA (USER32.315)
567 BOOL WINAPI
GrayStringA( HDC hdc
, HBRUSH hbr
, GRAYSTRINGPROC gsprc
,
568 LPARAM lParam
, INT cch
, INT x
, INT y
,
571 return TEXT_GrayString(hdc
, hbr
, gsprc
, lParam
, cch
, x
, y
, cx
, cy
, FALSE
, TRUE
);
575 /***********************************************************************
576 * GrayStringW (USER32.316)
578 BOOL WINAPI
GrayStringW( HDC hdc
, HBRUSH hbr
, GRAYSTRINGPROC gsprc
,
579 LPARAM lParam
, INT cch
, INT x
, INT y
,
582 return TEXT_GrayString(hdc
, hbr
, gsprc
, lParam
, cch
, x
, y
, cx
, cy
, TRUE
, TRUE
);
585 /***********************************************************************
588 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
589 * Note: this doesn't work too well for text-alignment modes other
590 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
592 static LONG
TEXT_TabbedTextOut( HDC hdc
, INT x
, INT y
, LPCSTR lpstr
,
593 INT count
, INT cTabStops
, const INT16
*lpTabPos16
,
594 const INT
*lpTabPos32
, INT nTabOrg
,
607 defWidth
= lpTabPos32
? *lpTabPos32
: *lpTabPos16
;
613 GetTextMetricsA( hdc
, &tm
);
614 defWidth
= 8 * tm
.tmAveCharWidth
;
619 for (i
= 0; i
< count
; i
++)
620 if (lpstr
[i
] == '\t') break;
621 GetTextExtentPointA( hdc
, lpstr
, i
, &extent
);
624 while ((cTabStops
> 0) &&
625 (nTabOrg
+ *lpTabPos32
<= x
+ extent
.cx
))
633 while ((cTabStops
> 0) &&
634 (nTabOrg
+ *lpTabPos16
<= x
+ extent
.cx
))
641 tabPos
= x
+ extent
.cx
;
642 else if (cTabStops
> 0)
643 tabPos
= nTabOrg
+ (lpTabPos32
? *lpTabPos32
: *lpTabPos16
);
645 tabPos
= nTabOrg
+ ((x
+ extent
.cx
- nTabOrg
) / defWidth
+ 1) * defWidth
;
652 r
.bottom
= y
+ extent
.cy
;
653 ExtTextOutA( hdc
, x
, y
,
654 GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
655 &r
, lpstr
, i
, NULL
);
661 return MAKELONG(tabPos
- start
, extent
.cy
);
665 /***********************************************************************
666 * TabbedTextOut16 (USER.196)
668 LONG WINAPI
TabbedTextOut16( HDC16 hdc
, INT16 x
, INT16 y
, LPCSTR lpstr
,
669 INT16 count
, INT16 cTabStops
,
670 const INT16
*lpTabPos
, INT16 nTabOrg
)
672 TRACE("%04x %d,%d '%.*s' %d\n",
673 hdc
, x
, y
, count
, lpstr
, count
);
674 return TEXT_TabbedTextOut( hdc
, x
, y
, lpstr
, count
, cTabStops
,
675 lpTabPos
, NULL
, nTabOrg
, TRUE
);
679 /***********************************************************************
680 * TabbedTextOutA (USER32.542)
682 LONG WINAPI
TabbedTextOutA( HDC hdc
, INT x
, INT y
, LPCSTR lpstr
,
683 INT count
, INT cTabStops
,
684 const INT
*lpTabPos
, INT nTabOrg
)
686 TRACE("%04x %d,%d '%.*s' %d\n",
687 hdc
, x
, y
, count
, lpstr
, count
);
688 return TEXT_TabbedTextOut( hdc
, x
, y
, lpstr
, count
, cTabStops
,
689 NULL
, lpTabPos
, nTabOrg
, TRUE
);
693 /***********************************************************************
694 * TabbedTextOutW (USER32.543)
696 LONG WINAPI
TabbedTextOutW( HDC hdc
, INT x
, INT y
, LPCWSTR str
,
697 INT count
, INT cTabStops
,
698 const INT
*lpTabPos
, INT nTabOrg
)
703 UINT codepage
= CP_ACP
; /* FIXME: get codepage of font charset */
705 acount
= WideCharToMultiByte(codepage
,0,str
,count
,NULL
,0,NULL
,NULL
);
706 p
= HeapAlloc( GetProcessHeap(), 0, acount
);
707 if(p
== NULL
) return 0; /* FIXME: is this the correct return on failure */
708 acount
= WideCharToMultiByte(codepage
,0,str
,count
,p
,acount
,NULL
,NULL
);
709 ret
= TabbedTextOutA( hdc
, x
, y
, p
, acount
, cTabStops
,
711 HeapFree( GetProcessHeap(), 0, p
);
716 /***********************************************************************
717 * GetTabbedTextExtent16 (USER.197)
719 DWORD WINAPI
GetTabbedTextExtent16( HDC16 hdc
, LPCSTR lpstr
, INT16 count
,
720 INT16 cTabStops
, const INT16
*lpTabPos
)
722 TRACE("%04x '%.*s' %d\n",
723 hdc
, count
, lpstr
, count
);
724 return TEXT_TabbedTextOut( hdc
, 0, 0, lpstr
, count
, cTabStops
,
725 lpTabPos
, NULL
, 0, FALSE
);
729 /***********************************************************************
730 * GetTabbedTextExtentA (USER32.293)
732 DWORD WINAPI
GetTabbedTextExtentA( HDC hdc
, LPCSTR lpstr
, INT count
,
733 INT cTabStops
, const INT
*lpTabPos
)
735 TRACE("%04x '%.*s' %d\n",
736 hdc
, count
, lpstr
, count
);
737 return TEXT_TabbedTextOut( hdc
, 0, 0, lpstr
, count
, cTabStops
,
738 NULL
, lpTabPos
, 0, FALSE
);
742 /***********************************************************************
743 * GetTabbedTextExtentW (USER32.294)
745 DWORD WINAPI
GetTabbedTextExtentW( HDC hdc
, LPCWSTR lpstr
, INT count
,
746 INT cTabStops
, const INT
*lpTabPos
)
751 UINT codepage
= CP_ACP
; /* FIXME: get codepage of font charset */
753 acount
= WideCharToMultiByte(codepage
,0,lpstr
,count
,NULL
,0,NULL
,NULL
);
754 p
= HeapAlloc( GetProcessHeap(), 0, acount
);
755 if(p
== NULL
) return 0; /* FIXME: is this the correct failure value? */
756 acount
= WideCharToMultiByte(codepage
,0,lpstr
,count
,p
,acount
,NULL
,NULL
);
757 ret
= GetTabbedTextExtentA( hdc
, p
, acount
, cTabStops
, lpTabPos
);
758 HeapFree( GetProcessHeap(), 0, p
);