Newer
Older
/*
* USER text functions
*
* Copyright 1993, 1994 Alexandre Julliard
*
* Contains
* 1. DrawText functions
* 2. GrayString functions
* 3. TabbedText functions
*/
#include <string.h>
#include <assert.h>
#include "windef.h"
#include "wingdi.h"
#include "wine/winuser16.h"
#include "wine/unicode.h"
#include "winbase.h"
#include "winerror.h"
#include "winnls.h"
#include "user.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(text);
#define countof(a) (sizeof(a)/sizeof(a[0]))
/*********************************************************************
*
* DrawText functions
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
*
* Design issues
* How many buffers to use
* While processing in DrawText there are potentially three different forms
* of the text that need to be held. How are they best held?
* 1. The original text is needed, of course, to see what to display.
* 2. The text that will be returned to the user if the DT_MODIFYSTRING is
* in effect.
* 3. The buffered text that is about to be displayed e.g. the current line.
* Typically this will exclude the ampersands used for prefixing etc.
*
* Complications.
* a. If the buffered text to be displayed includes the ampersands then
* we will need special measurement and draw functions that will ignore
* the ampersands (e.g. by copying to a buffer without the prefix and
* then using the normal forms). This may involve less space but may
* require more processing. e.g. since a line containing tabs may
* contain several underlined characters either we need to carry around
* a list of prefix locations or we may need to locate them several
* times.
* b. If we actually directly modify the "original text" as we go then we
* will need some special "caching" to handle the fact that when we
* ellipsify the text the ellipsis may modify the next line of text,
* which we have not yet processed. (e.g. ellipsification of a W at the
* end of a line will overwrite the W, the \n and the first character of
* the next line, and a \0 will overwrite the second. Try it!!)
*
* Option 1. Three separate storages. (To be implemented)
* If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
* the edited string in some form, either as the string itself or as some
* sort of "edit list" to be applied just before returning.
* Use a buffer that holds the ellipsified current line sans ampersands
* and accept the need occasionally to recalculate the prefixes (if
* DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
#define TAB 9
#define LF 10
#define CR 13
#define SPACE 32
#define PREFIX 38
#define FORWARD_SLASH '/'
#define BACK_SLASH '\\'
static const WCHAR ELLIPSISW[] = {'.','.','.', 0};
static int tabwidth;
static int prefix_offset;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*********************************************************************
* TEXT_Ellipsify (static)
*
* Add an ellipsis to the end of the given string whilst ensuring it fits.
*
* If the ellipsis alone doesn't fit then it will be returned anyway.
*
* See Also TEXT_PathEllipsify
*
* Arguments
* hdc [in] The handle to the DC that defines the font.
* str [in/out] The string that needs to be modified.
* max_str [in] The dimension of str (number of WCHAR).
* len_str [in/out] The number of characters in str
* width [in] The maximum width permitted (in logical coordinates)
* size [out] The dimensions of the text
* modstr [out] The modified form of the string, to be returned to the
* calling program. It is assumed that the caller has
* made sufficient space available so we don't need to
* know the size of the space. This pointer may be NULL if
* the modified string is not required.
* len_before [out] The number of characters before the ellipsis.
* len_ellip [out] The number of characters in the ellipsis.
*
* See for example Microsoft article Q249678.
*
* For now we will simply use three dots rather than worrying about whether
* the font contains an explicit ellipsis character.
*/
static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len,
unsigned int *len_str, int width, SIZE *size,
WCHAR *modstr,
int *len_before, int *len_ellip)
{
unsigned int len_ellipsis;
len_ellipsis = strlenW (ELLIPSISW);
if (len_ellipsis > max_len) len_ellipsis = max_len;
if (*len_str > max_len - len_ellipsis)
*len_str = max_len - len_ellipsis;
for ( ; ; )
{
strncpyW (str + *len_str, ELLIPSISW, len_ellipsis);
if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
NULL, NULL, size)) break;
if (!*len_str || size->cx <= width) break;
(*len_str)--;
}
*len_ellip = len_ellipsis;
*len_before = *len_str;
*len_str += len_ellipsis;
if (modstr)
{
strncpyW (modstr, str, *len_str);
*(str+*len_str) = '\0';
}
}
/*********************************************************************
* TEXT_PathEllipsify (static)
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
*
* Add an ellipsis to the provided string in order to make it fit within
* the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
* flag.
*
* See Also TEXT_Ellipsify
*
* Arguments
* hdc [in] The handle to the DC that defines the font.
* str [in/out] The string that needs to be modified
* max_str [in] The dimension of str (number of WCHAR).
* len_str [in/out] The number of characters in str
* width [in] The maximum width permitted (in logical coordinates)
* size [out] The dimensions of the text
* modstr [out] The modified form of the string, to be returned to the
* calling program. It is assumed that the caller has
* made sufficient space available so we don't need to
* know the size of the space. This pointer may be NULL if
* the modified string is not required.
* len_before [out] The number of characters before the ellipsis.
* len_ellip [out] The number of characters in the ellipsis.
* len_after [out] The number of characters after the ellipsis.
*
* For now we will simply use three dots rather than worrying about whether
* the font contains an explicit ellipsis character.
*
* The following applies, I think to Win95. We will need to extend it for
* Win98 which can have both path and end ellipsis at the same time (e.g.
* C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
*
* The resulting string consists of as much as possible of the following:
* 1. The ellipsis itself
* 2. The last \ or / of the string (if any)
* 3. Everything after the last \ or / of the string (if any) or the whole
* string if there is no / or \. I believe that under Win95 this would
* include everything even though some might be clipped off the end whereas
* under Win98 that might be ellipsified too. (Not yet implemented). Yet
* to be investigated is whether this would include wordbreaking if the
* filename is more than 1 word and splitting if DT_EDITCONTROL was in
* effect. (If DT_EDITCONTROL is in effect then on occasions text will be
* broken within words).
* 4. All the stuff before the / or \, which is placed before the ellipsis.
*/
static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len,
unsigned int *len_str, int width, SIZE *size,
WCHAR *modstr,
int *len_before, int *len_ellip, int *len_after)
{
int len_ellipsis;
int len_trailing;
WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash;
len_ellipsis = strlenW (ELLIPSISW);
if (!max_len) return;
if (len_ellipsis >= max_len) len_ellipsis = max_len - 1;
if (*len_str + len_ellipsis >= max_len)
*len_str = max_len - len_ellipsis-1;
/* Hopefully this will never happen, otherwise it would probably lose
* the wrong character
*/
str[*len_str] = '\0'; /* to simplify things */
lastBkSlash = strrchrW (str, BACK_SLASH);
lastFwdSlash = strrchrW (str, FORWARD_SLASH);
lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash;
if (!lastSlash) lastSlash = str;
len_trailing = *len_str - (lastSlash - str);
/* overlap-safe movement to the right */
memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR));
strncpyW (lastSlash, ELLIPSISW, len_ellipsis);
len_trailing += len_ellipsis;
/* From this point on lastSlash actually points to the ellipsis in front
* of the last slash and len_trailing includes the ellipsis
*/
for ( ; ; )
{
if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
NULL, NULL, size)) break;
if (lastSlash == str || size->cx <= width) break;
/* overlap-safe movement to the left */
memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR));
lastSlash--;
assert (*len_str);
(*len_str)--;
}
*len_before = lastSlash-str;
*len_ellip = len_ellipsis;
*len_after = len_trailing - len_ellipsis;
*len_str += len_ellipsis;
if (modstr)
{
strncpyW (modstr, str, *len_str);
*(str+*len_str) = '\0';
}
}
void TEXT_CopySansPrefix (int noprefix, WCHAR *d, int *len_d, const WCHAR *s, int len_s)
{
if (noprefix)
{
strncpyW (d, s, len_s);
*len_d = len_s;
}
else
{
*len_d = 0;
while (len_s--)
{
if (*s == PREFIX && len_s)
{
len_s--;
s++;
}
*d++ = *s++;
(*len_d)++;
}
}
}
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/*********************************************************************
* TEXT_Reprefix
*
* Reanalyse the text to find the prefixed character. This is called when
* wordbreaking or ellipsification has shortened the string such that the
* previously noted prefixed character is no longer visible.
*
* Parameters
* str [in] The original string segment (including all characters)
* n1 [in] The number of characters visible before the path ellipsis
* n2 [in] The number of characters replaced by the path ellipsis
* ne [in] The number of characters in the path ellipsis, ignored if
* n2 is zero
* n3 [in] The number of characters visible after the path ellipsis
*
* Return Values
* The prefix offset within the new string segment (the one that contains the
* ellipses and does not contain the prefix characters) (-1 if none)
*
* Remarks
* We know that n1+n2+n3 must be strictly less than the length of the segment
* (because otherwise there would be no need to call this function)
*/
static int TEXT_Reprefix (const WCHAR *str, unsigned int n1, unsigned int n2,
unsigned int ne, unsigned int n3)
{
int result = -1;
unsigned int i = 0;
unsigned int n = n1 + n2 + n3;
if (!n2) ne = 0;
while (i < n)
{
if (i == n1)
{
/* Reached the path ellipsis; jump over it */
str += n2;
i += n2;
if (!n3) break; /* Nothing after the path ellipsis */
}
if (*str++ == PREFIX)
{
result = (i < n1) ? i : i - n2 + ne;
str++;
}
else;
i++;
}
return result;
}
/*********************************************************************
* Return next line of text from a string.
*
* hdc - handle to DC.
* str - string to parse into lines.
* count - length of str.
* dest - destination in which to return line.
* len - dest buffer size in chars on input, copied length into dest on output.
* width - maximum width of line in pixels.
* format - format type passed to DrawText.
*
* Returns pointer to next char in str after end of the line
* or NULL if end of str reached.
*
* FIXME:
* GetTextExtentPoint is used to get the width of each character,
* rather than GetCharABCWidth... So the whitespace between
* characters is ignored, and the reported len is too great.
*/
static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count,
WCHAR *dest, int *len, int width, WORD format)
int i = 0, j = 0;
int plen = 0;
SIZE size;
int wb_i = 0, wb_j = 0, wb_count = 0;
int normal_char;
normal_char = 1;
switch (str[i])
{
case CR:
case LF:
if (!(format & DT_SINGLELINE))
{
if ((*count > 1) && (((str[i] == CR) && (str[i+1] == LF)) ||
((str[i] == LF) && (str[i+1] == CR))))
{
(*count)--;
i++;
}
i++;
*len = j;
(*count)--;
return (&str[i]);
}
/* else it's just another character */
break;
case PREFIX:
if (!(format & DT_NOPREFIX) && *count > 1)
{
if (str[++i] == PREFIX)
(*count)--; /* and treat it as just another character */
else {
prefix_offset = j;
normal_char = 0; /* we are skipping the PREFIX itself */
break;
}
}
break;
case TAB:
if (format & DT_EXPANDTABS)
{
if ((format & DT_WORDBREAK))
{
wb_i = i+1;
wb_j = j;
wb_count = *count;
plen = ((plen/tabwidth)+1)*tabwidth;
normal_char = 0;
dest[j++] = str[i++];
}
break;
case SPACE:
if ((format & DT_WORDBREAK))
wb_i = i+1;
wb_j = j;
wb_count = *count;
break;
default:
}
if (normal_char)
{
dest[j++] = str[i++];
if ((format & DT_WORDBREAK))
if (!GetTextExtentPointW(hdc, &dest[j-1], 1, &size))
return NULL;
plen += size.cx;
}
(*count)--;
if ((format & DT_WORDBREAK) && plen > width && wb_j)
{
*len = wb_j;
*count = wb_count - 1;
return (&str[wb_i]);
}
}
*len = j;
return NULL;
}
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/***********************************************************************
* TEXT_DrawUnderscore
*
* Draw the underline under the prefixed character
*
* Parameters
* hdc [in] The handle of the DC for drawing
* x [in] The x location of the line segment (logical coordinates)
* y [in] The y location of where the underscore should appear
* (logical coordinates)
* str [in] The text of the line segment
* offset [in] The offset of the underscored character within str
*/
static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset)
{
int prefix_x;
int prefix_end;
SIZE size;
HPEN hpen;
HPEN oldPen;
GetTextExtentPointW (hdc, str, offset, &size);
prefix_x = x + size.cx;
GetTextExtentPointW (hdc, str, offset+1, &size);
prefix_end = x + size.cx - 1;
/* The above method may eventually be slightly wrong due to kerning etc. */
hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc));
oldPen = SelectObject (hdc, hpen);
MoveToEx (hdc, prefix_x, y, NULL);
LineTo (hdc, prefix_end, y);
SelectObject (hdc, oldPen);
DeleteObject (hpen);
}
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/* We are only going to need this until we correct the measuring in
* TEXT_NextLineW (coming soon)
*/
static int TEXT_TextExtent (HDC hdc, UINT flags, const WCHAR *line, int len, SIZE *size)
{
if ((flags & DT_EXPANDTABS))
{
SIZE tsize;
const WCHAR *p;
size->cx = 0;
size->cy = 0;
while (len)
{
p = line; while (p < line+len && *p != TAB) p++;
if (!GetTextExtentPointW (hdc, line, p-line, &tsize)) return 0;
size->cx += tsize.cx;
if (tsize.cy > size->cy) size->cy = tsize.cy;
len -= (p-line);
line=p;
if (len)
{
assert (*line == TAB);
len--;
line++;
size->cx = ((size->cx/tabwidth)+1)*tabwidth;
}
}
return 1;
}
else
return GetTextExtentPointW(hdc, line, len, size);
}
/***********************************************************************
*
* The documentation on the extra space required for DT_MODIFYSTRING at MSDN
* is not quite complete, especially with regard to \0. We will assume that
* the returned string could have a length of up to i_count+3 and also have
* a trailing \0 (which would be 4 more than a not-null-terminated string but
* 3 more than a null-terminated string). If this is not so then increase
* the allowance in DrawTextExA.
#define MAX_STATIC_BUFFER 1024
INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
{
SIZE size;
const WCHAR *strPtr;
static WCHAR line[MAX_STATIC_BUFFER];
int len, lh, count=i_count;
TEXTMETRICW tm;
int lmargin = 0, rmargin = 0;
int x = rect->left, y = rect->top;
int width = rect->right - rect->left;
int max_width = 0;
TRACE("%s, %d , [(%d,%d),(%d,%d)]\n", debugstr_wn (str, count), count,
rect->left, rect->top, rect->right, rect->bottom);
if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin);
if (!str) return 0;
if (count == -1) count = strlenW(str);
if (count == 0) return 0;
strPtr = str;
GetTextMetricsW(hdc, &tm);
if (flags & DT_EXTERNALLEADING)
lh = tm.tmHeight + tm.tmExternalLeading;
else
lh = tm.tmHeight;
if (dtp)
{
lmargin = dtp->iLeftMargin * tm.tmAveCharWidth;
rmargin = dtp->iRightMargin * tm.tmAveCharWidth;
if (!(flags & (DT_CENTER | DT_RIGHT)))
x += lmargin;
dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */
}
if (flags & DT_EXPANDTABS)
{
int tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8;
tabwidth = tm.tmAveCharWidth * tabstop;
}
if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
do
{
prefix_offset = -1;
strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags);
if (!TEXT_TextExtent(hdc, flags, line, len, &size)) return 0;
if (flags & DT_CENTER) x = (rect->left + rect->right -
size.cx) / 2;
else if (flags & DT_RIGHT) x = rect->right - size.cx;
if (flags & DT_SINGLELINE)
{
if (flags & DT_VCENTER) y = rect->top +
(rect->bottom - rect->top) / 2 - size.cy / 2;
else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
if ((flags & DT_SINGLELINE) && size.cx > width &&
(flags & (DT_PATH_ELLIPSIS | DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)))
{
WCHAR tmpstr[countof(line)];
int len_before_ellipsis;
int len_ellipsis;
int len_after_ellipsis;
if ((flags & DT_EXPANDTABS))
{
/* If there are tabs then we must only ellipsify the last
* segment. That will become trivial when we move the
* ellipsification into the TEXT_NextLineW (coming soon)
*/
const WCHAR *p;
p = line + len;
while (p > line)
if (*(--p) == TAB)
{
FIXME ("Ellipsification of single line tabbed strings will be fixed shortly\n");
break;
}
}
if (flags & DT_PATH_ELLIPSIS)
{
/* We may need to remeasure the string because it was clipped */
if ((flags & DT_WORDBREAK) || !(flags & DT_NOCLIP))
TEXT_CopySansPrefix ((flags & DT_NOPREFIX), line, &len, str, i_count >= 0 ? i_count : strlenW(str));
TEXT_PathEllipsify (hdc, line, countof(line), &len, width, &size, tmpstr, &len_before_ellipsis, &len_ellipsis, &len_after_ellipsis);
}
else
{
TEXT_Ellipsify (hdc, line, countof(line), &len, width, &size, tmpstr, &len_before_ellipsis, &len_ellipsis);
len_after_ellipsis = 0;
}
strPtr = NULL;
if (prefix_offset >= 0 &&
prefix_offset >= len_before_ellipsis)
prefix_offset = TEXT_Reprefix (str, len_before_ellipsis, strlenW(str)-len_ellipsis-len_before_ellipsis-len_after_ellipsis, len_ellipsis, len_after_ellipsis);
if (flags & DT_MODIFYSTRING)
strcpyW(str, tmpstr);
}
if (!(flags & DT_CALCRECT))
{
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
const WCHAR *str = line;
int xseg = x;
while (len)
{
int len_seg;
SIZE size;
if ((flags & DT_EXPANDTABS))
{
const WCHAR *p;
p = str; while (p < str+len && *p != TAB) p++;
len_seg = p - str;
if (len_seg != len && !GetTextExtentPointW(hdc, str, len_seg, &size))
return 0;
}
else
len_seg = len;
if (!ExtTextOutW( hdc, xseg, y,
((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) |
((flags & DT_RTLREADING) ? ETO_RTLREADING : 0),
rect, str, len_seg, NULL )) return 0;
if (prefix_offset != -1 && prefix_offset < len_seg)
{
TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset);
}
len -= len_seg;
str += len_seg;
if (len)
{
assert ((flags & DT_EXPANDTABS) && *str == TAB);
len--; str++;
xseg += ((size.cx/tabwidth)+1)*tabwidth;
if (prefix_offset != -1)
{
if (prefix_offset < len_seg)
{
/* We have just drawn an underscore; we ought to
* figure out where the next one is. I am going
* to leave it for now until I have a better model
* for the line, which will make reprefixing easier
*/
prefix_offset = -1;
}
else
prefix_offset -= len_seg;
}
}
}
}
else if (size.cx > max_width)
max_width = size.cx;
y += lh;
if (strPtr)
{
if (!(flags & DT_NOCLIP))
{
if (y > rect->bottom - lh)
break;
}
}
if (dtp)
dtp->uiLengthDrawn += len;
}
while (strPtr);
if (flags & DT_CALCRECT)
{
rect->right = rect->left + max_width;
rect->bottom = y;
if (dtp)
rect->right += lmargin + rmargin;
}
return y - rect->top;
}
/***********************************************************************
*
* If DT_MODIFYSTRING is specified then there must be room for up to
* 4 extra characters. We take great care about just how much modified
* string we return.
INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
{
WCHAR *wstr;
WCHAR *p;
INT ret = 0;
DWORD wcount;
DWORD wmax;
DWORD amax;
if (count == -1) count = strlen(str);
if (!count) return 0;
wcount = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
wmax = wcount;
amax = count;
if (flags & DT_MODIFYSTRING)
{
wmax += 4;
amax += 4;
}
wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
if (wstr)
{
MultiByteToWideChar( CP_ACP, 0, str, count, wstr, wcount );
if (flags & DT_MODIFYSTRING)
for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE;
/* Initialise the extra characters so that we can see which ones
* change. U+FFFE is guaranteed to be not a unicode character and
* so will not be generated by DrawTextEx itself.
*/
ret = DrawTextExW( hdc, wstr, wcount, rect, flags, NULL );
{
/* Unfortunately the returned string may contain multiple \0s
* and so we need to measure it ourselves.
*/
for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
WideCharToMultiByte( CP_ACP, 0, wstr, wcount, str, amax, NULL, NULL );
}
HeapFree(GetProcessHeap(), 0, wstr);
}
return ret;
}
/***********************************************************************
INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
DRAWTEXTPARAMS dtp;
memset (&dtp, 0, sizeof(dtp));
if (flags & DT_TABSTOP)
{
dtp.iTabLength = (flags >> 8) && 0xff;
flags &= 0xffff00ff;
}
return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
}
/***********************************************************************
INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
DRAWTEXTPARAMS dtp;
memset (&dtp, 0, sizeof(dtp));
if (flags & DT_TABSTOP)
{
dtp.iTabLength = (flags >> 8) && 0xff;
flags &= 0xffff00ff;
}
return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
}
/***********************************************************************
* DrawText (USER.85)
*/
INT16 WINAPI DrawText16( HDC16 hdc, LPCSTR str, INT16 count, LPRECT16 rect, UINT16 flags )
{
INT16 ret;
if (rect)
{
RECT rect32;
CONV_RECT16TO32( rect, &rect32 );
ret = DrawTextA( hdc, str, count, &rect32, flags );
CONV_RECT32TO16( &rect32, rect );
}
else ret = DrawTextA( hdc, str, count, NULL, flags);
return ret;
}
/***********************************************************************
*
* GrayString functions
*/
/* ### start build ### */
extern WORD CALLBACK TEXT_CallTo16_word_wlw(GRAYSTRINGPROC16,WORD,LONG,WORD);
/* ### stop build ### */
struct gray_string_info
{
GRAYSTRINGPROC16 proc;
LPARAM param;
};
/* callback for 16-bit gray string proc */
static BOOL CALLBACK gray_string_callback( HDC hdc, LPARAM param, INT len )
{
const struct gray_string_info *info = (struct gray_string_info *)param;
return TEXT_CallTo16_word_wlw( info->proc, hdc, info->param, len );
}
/***********************************************************************
* TEXT_GrayString
*
* FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
* heap and we can guarantee that the handles fit in an INT16. We have to
* rethink the strategy once the migration to NT handles is complete.
* We are going to get a lot of code-duplication once this migration is
* completed...
*
*/
static BOOL TEXT_GrayString(HDC hdc, HBRUSH hb, GRAYSTRINGPROC fn, LPARAM lp, INT len,
INT x, INT y, INT cx, INT cy, BOOL unicode, BOOL _32bit)
{
HBITMAP hbm, hbmsave;
HBRUSH hbsave;
HFONT hfsave;
int slen = len;
BOOL retval = TRUE;
COLORREF fg, bg;
if(!hdc) return FALSE;
if (!(memdc = CreateCompatibleDC(hdc))) return FALSE;
if(len == 0)
{
if(unicode)
slen = lstrlenW((LPCWSTR)lp);
else if(_32bit)
slen = strlen((LPCSTR)lp);
else
slen = strlen(MapSL(lp));
}
if((cx == 0 || cy == 0) && slen != -1)
{
SIZE s;
if(unicode)
GetTextExtentPoint32W(hdc, (LPCWSTR)lp, slen, &s);
else if(_32bit)
GetTextExtentPoint32A(hdc, (LPCSTR)lp, slen, &s);
else
GetTextExtentPoint32A(hdc, MapSL(lp), slen, &s);
if(cx == 0) cx = s.cx;
if(cy == 0) cy = s.cy;
}
hbm = CreateBitmap(cx, cy, 1, 1, NULL);
hbmsave = (HBITMAP)SelectObject(memdc, hbm);
hbsave = SelectObject( memdc, GetStockObject(BLACK_BRUSH) );
PatBlt( memdc, 0, 0, cx, cy, PATCOPY );
SelectObject( memdc, hbsave );
SetTextColor(memdc, RGB(255, 255, 255));
SetBkColor(memdc, RGB(0, 0, 0));
hfsave = (HFONT)SelectObject(memdc, GetCurrentObject(hdc, OBJ_FONT));
if(fn)
{
if(_32bit)
retval = fn(memdc, lp, slen);
else
retval = (BOOL)((BOOL16)((GRAYSTRINGPROC16)fn)((HDC16)memdc, lp, (INT16)slen));
}
else
{
if(unicode)
TextOutW(memdc, 0, 0, (LPCWSTR)lp, slen);
else if(_32bit)
TextOutA(memdc, 0, 0, (LPCSTR)lp, slen);
else
TextOutA(memdc, 0, 0, MapSL(lp), slen);
}
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
SelectObject(memdc, hfsave);
/*
* Windows doc says that the bitmap isn't grayed when len == -1 and
* the callback function returns FALSE. However, testing this on
* win95 showed otherwise...
*/
#ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
if(retval || len != -1)
#endif
{
hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
PatBlt(memdc, 0, 0, cx, cy, 0x000A0329);
SelectObject(memdc, hbsave);
}
if(hb) hbsave = (HBRUSH)SelectObject(hdc, hb);
fg = SetTextColor(hdc, RGB(0, 0, 0));
bg = SetBkColor(hdc, RGB(255, 255, 255));
BitBlt(hdc, x, y, cx, cy, memdc, 0, 0, 0x00E20746);
SetTextColor(hdc, fg);
SetBkColor(hdc, bg);
if(hb) SelectObject(hdc, hbsave);
SelectObject(memdc, hbmsave);
DeleteObject(hbm);
DeleteDC(memdc);
return retval;
}
/***********************************************************************
*/
BOOL16 WINAPI GrayString16( HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc,
LPARAM lParam, INT16 cch, INT16 x, INT16 y,
INT16 cx, INT16 cy )
{
struct gray_string_info info;
if (!gsprc) return TEXT_GrayString(hdc, hbr, NULL, lParam, cch, x, y, cx, cy, FALSE, FALSE);
info.proc = gsprc;
info.param = lParam;
return TEXT_GrayString( hdc, hbr, gray_string_callback, (LPARAM)&info,
cch, x, y, cx, cy, FALSE, FALSE);
}
/***********************************************************************
*/
BOOL WINAPI GrayStringA( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
LPARAM lParam, INT cch, INT x, INT y,
INT cx, INT cy )
return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
FALSE, TRUE);
}
/***********************************************************************
*/
BOOL WINAPI GrayStringW( HDC hdc, HBRUSH hbr, GRAYSTRINGPROC gsprc,
LPARAM lParam, INT cch, INT x, INT y,
INT cx, INT cy )
return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy,
TRUE, TRUE);
}
/***********************************************************************
*
* TabbedText functions
*/
/***********************************************************************
* TEXT_TabbedTextOut
*
* Helper function for TabbedTextOut() and GetTabbedTextExtent().
* Note: this doesn't work too well for text-alignment modes other
* than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
*/
static LONG TEXT_TabbedTextOut( HDC hdc, INT x, INT y, LPCSTR lpstr,
INT count, INT cTabStops, const INT16 *lpTabPos16,
const INT *lpTabPos32, INT nTabOrg,
BOOL fDisplayText )
{
INT defWidth;
SIZE extent;
int i, tabPos = x;