4 * Copyright 1993, 1994 Alexandre Julliard
5 * Copyright 2002 Bill Medland
8 * 1. DrawText functions
9 * 2. GrayString functions
10 * 3. TabbedText functions
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "wine/winuser16.h"
33 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(text
);
42 /*********************************************************************
47 * How many buffers to use
48 * While processing in DrawText there are potentially three different forms
49 * of the text that need to be held. How are they best held?
50 * 1. The original text is needed, of course, to see what to display.
51 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
53 * 3. The buffered text that is about to be displayed e.g. the current line.
54 * Typically this will exclude the ampersands used for prefixing etc.
57 * a. If the buffered text to be displayed includes the ampersands then
58 * we will need special measurement and draw functions that will ignore
59 * the ampersands (e.g. by copying to a buffer without the prefix and
60 * then using the normal forms). This may involve less space but may
61 * require more processing. e.g. since a line containing tabs may
62 * contain several underlined characters either we need to carry around
63 * a list of prefix locations or we may need to locate them several
65 * b. If we actually directly modify the "original text" as we go then we
66 * will need some special "caching" to handle the fact that when we
67 * ellipsify the text the ellipsis may modify the next line of text,
68 * which we have not yet processed. (e.g. ellipsification of a W at the
69 * end of a line will overwrite the W, the \n and the first character of
70 * the next line, and a \0 will overwrite the second. Try it!!)
72 * Option 1. Three separate storages. (To be implemented)
73 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
74 * the edited string in some form, either as the string itself or as some
75 * sort of "edit list" to be applied just before returning.
76 * Use a buffer that holds the ellipsified current line sans ampersands
77 * and accept the need occasionally to recalculate the prefixes (if
78 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
87 #define FORWARD_SLASH '/'
88 #define BACK_SLASH '\\'
90 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
92 /* These will have to go into a structure to be passed around rather than
93 * sitting in static memory
96 static int prefix_offset
;
97 static int len_before_ellipsis
, len_ellipsis
, len_under_ellipsis
,
100 /*********************************************************************
101 * TEXT_Ellipsify (static)
103 * Add an ellipsis to the end of the given string whilst ensuring it fits.
105 * If the ellipsis alone doesn't fit then it will be returned anyway.
107 * See Also TEXT_PathEllipsify
110 * hdc [in] The handle to the DC that defines the font.
111 * str [in/out] The string that needs to be modified.
112 * max_str [in] The dimension of str (number of WCHAR).
113 * len_str [in/out] The number of characters in str
114 * width [in] The maximum width permitted (in logical coordinates)
115 * size [out] The dimensions of the text
116 * modstr [out] The modified form of the string, to be returned to the
117 * calling program. It is assumed that the caller has
118 * made sufficient space available so we don't need to
119 * know the size of the space. This pointer may be NULL if
120 * the modified string is not required.
121 * len_before [out] The number of characters before the ellipsis.
122 * len_ellip [out] The number of characters in the ellipsis.
124 * See for example Microsoft article Q249678.
126 * For now we will simply use three dots rather than worrying about whether
127 * the font contains an explicit ellipsis character.
129 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
130 unsigned int *len_str
, int width
, SIZE
*size
,
132 int *len_before
, int *len_ellip
)
134 unsigned int len_ellipsis
;
136 len_ellipsis
= strlenW (ELLIPSISW
);
137 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
138 if (*len_str
> max_len
- len_ellipsis
)
139 *len_str
= max_len
- len_ellipsis
;
143 strncpyW (str
+ *len_str
, ELLIPSISW
, len_ellipsis
);
145 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
146 NULL
, NULL
, size
)) break;
148 if (!*len_str
|| size
->cx
<= width
) break;
152 *len_ellip
= len_ellipsis
;
153 *len_before
= *len_str
;
154 *len_str
+= len_ellipsis
;
158 strncpyW (modstr
, str
, *len_str
);
159 *(str
+*len_str
) = '\0';
163 /*********************************************************************
164 * TEXT_PathEllipsify (static)
166 * Add an ellipsis to the provided string in order to make it fit within
167 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
170 * See Also TEXT_Ellipsify
173 * hdc [in] The handle to the DC that defines the font.
174 * str [in/out] The string that needs to be modified
175 * max_str [in] The dimension of str (number of WCHAR).
176 * len_str [in/out] The number of characters in str
177 * width [in] The maximum width permitted (in logical coordinates)
178 * size [out] The dimensions of the text
179 * modstr [out] The modified form of the string, to be returned to the
180 * calling program. It is assumed that the caller has
181 * made sufficient space available so we don't need to
182 * know the size of the space. This pointer may be NULL if
183 * the modified string is not required.
184 * len_before [out] The number of characters before the ellipsis.
185 * len_ellip [out] The number of characters in the ellipsis.
186 * len_under [out] The number of characters replaced by the ellipsis.
187 * len_after [out] The number of characters after the ellipsis.
189 * For now we will simply use three dots rather than worrying about whether
190 * the font contains an explicit ellipsis character.
192 * The following applies, I think to Win95. We will need to extend it for
193 * Win98 which can have both path and end ellipsis at the same time (e.g.
194 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
196 * The resulting string consists of as much as possible of the following:
197 * 1. The ellipsis itself
198 * 2. The last \ or / of the string (if any)
199 * 3. Everything after the last \ or / of the string (if any) or the whole
200 * string if there is no / or \. I believe that under Win95 this would
201 * include everything even though some might be clipped off the end whereas
202 * under Win98 that might be ellipsified too. (Not yet implemented). Yet
203 * to be investigated is whether this would include wordbreaking if the
204 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
205 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
206 * broken within words).
207 * 4. All the stuff before the / or \, which is placed before the ellipsis.
209 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
210 unsigned int *len_str
, int width
, SIZE
*size
,
212 int *len_before
, int *len_ellip
, int *len_under
,
217 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
219 len_ellipsis
= strlenW (ELLIPSISW
);
220 if (!max_len
) return;
221 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
222 if (*len_str
+ len_ellipsis
>= max_len
)
223 *len_str
= max_len
- len_ellipsis
-1;
224 /* Hopefully this will never happen, otherwise it would probably lose
225 * the wrong character
227 str
[*len_str
] = '\0'; /* to simplify things */
229 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
230 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
231 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
232 if (!lastSlash
) lastSlash
= str
;
233 len_trailing
= *len_str
- (lastSlash
- str
);
235 /* overlap-safe movement to the right */
236 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
237 strncpyW (lastSlash
, ELLIPSISW
, len_ellipsis
);
238 len_trailing
+= len_ellipsis
;
239 /* From this point on lastSlash actually points to the ellipsis in front
240 * of the last slash and len_trailing includes the ellipsis
246 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
247 NULL
, NULL
, size
)) break;
249 if (lastSlash
== str
|| size
->cx
<= width
) break;
251 /* overlap-safe movement to the left */
252 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
259 *len_before
= lastSlash
-str
;
260 *len_ellip
= len_ellipsis
;
261 *len_after
= len_trailing
- len_ellipsis
;
262 *len_str
+= len_ellipsis
;
266 strncpyW (modstr
, str
, *len_str
);
267 *(str
+*len_str
) = '\0';
271 /*********************************************************************
272 * TEXT_WordBreak (static)
274 * Perform wordbreak processing on the given string
276 * Assumes that DT_WORDBREAK has been specified and not all the characters
277 * fit. Note that this function should even be called when the first character
278 * that doesn't fit is known to be a space or tab, so that it can swallow them.
280 * Note that the Windows processing has some strange properties.
281 * 1. If the text is left-justified and there is room for some of the spaces
282 * that follow the last word on the line then those that fit are included on
284 * 2. If the text is centred or right-justified and there is room for some of
285 * the spaces that follow the last word on the line then all but one of those
286 * that fit are included on the line.
287 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
288 * character of a new line it will be skipped.
291 * hdc [in] The handle to the DC that defines the font.
292 * str [in/out] The string that needs to be broken.
293 * max_str [in] The dimension of str (number of WCHAR).
294 * len_str [in/out] The number of characters in str
295 * width [in] The maximum width permitted
296 * format [in] The format flags in effect
297 * chars_fit [in] The maximum number of characters of str that are already
298 * known to fit; chars_fit+1 is known not to fit.
299 * chars_used [out] The number of characters of str that have been "used" and
300 * do not need to be included in later text. For example this will
301 * include any spaces that have been discarded from the start of
303 * size [out] The size of the returned text in logical coordinates
305 * Pedantic assumption - Assumes that the text length is monotonically
306 * increasing with number of characters (i.e. no weird kernings)
310 * Work back from the last character that did fit to either a space or the last
311 * character of a word, whichever is met first.
312 * If there was one or the first character didn't fit then
313 * If the text is centred or right justified and that one character was a
314 * space then break the line before that character
315 * Otherwise break the line after that character
316 * and if the next character is a space then discard it.
317 * Suppose there was none (and the first character did fit).
318 * If Break Within Word is permitted
319 * break the word after the last character that fits (there must be
320 * at least one; none is caught earlier).
322 * discard any trailing space.
323 * include the whole word; it may be ellipsified later
325 * Break Within Word is permitted under a set of circumstances that are not
326 * totally clear yet. Currently our best guess is:
327 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
328 * DT_PATH_ELLIPSIS is
331 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
332 unsigned int *len_str
,
333 int width
, int format
, unsigned int chars_fit
,
334 unsigned int *chars_used
, SIZE
*size
)
338 assert (format
& DT_WORDBREAK
);
339 assert (chars_fit
< *len_str
);
341 /* Work back from the last character that did fit to either a space or the
342 * last character of a word, whichever is met first.
344 p
= str
+ chars_fit
; /* The character that doesn't fit */
347 ; /* we pretend that it fits anyway */
348 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
349 p
--; /* the word just fitted */
352 while (p
> str
&& *(--p
) != SPACE
)
354 word_fits
= (p
!= str
|| *p
== SPACE
);
356 /* If there was one or the first character didn't fit then */
359 /* break the line before/after that character */
360 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
363 /* and if the next character is a space then discard it. */
364 *chars_used
= *len_str
;
368 /* Suppose there was none. */
371 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
374 /* break the word after the last character that fits (there must be
375 * at least one; none is caught earlier).
377 *len_str
= chars_fit
;
378 *chars_used
= chars_fit
;
380 /* FIXME - possible error. Since the next character is now removed
381 * this could make the text longer so that it no longer fits, and
382 * so we need a loop to test and shrink.
388 /* discard any trailing space. */
389 const WCHAR
*e
= str
+ *len_str
;
391 while (p
< e
&& *p
!= SPACE
)
393 *chars_used
= p
- str
;
394 if (p
< e
) /* i.e. loop failed because *p == SPACE */
397 /* include the whole word; it may be ellipsified later */
399 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
400 * so that it will be too long
404 /* Remeasure the string */
405 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
408 /*********************************************************************
411 * Skip over the given number of characters, bearing in mind prefix
412 * substitution and the fact that a character may take more than one
413 * WCHAR (Unicode surrogates are two words long) (and there may have been
417 * new_count [out] The updated count
418 * new_str [out] The updated pointer
419 * start_count [in] The count of remaining characters corresponding to the
420 * start of the string
421 * start_str [in] The starting point of the string
422 * max [in] The number of characters actually in this segment of the
423 * string (the & counts)
424 * n [in] The number of characters to skip (if prefix then
426 * prefix [in] Apply prefix substitution
432 * There must be at least n characters in the string
433 * We need max because the "line" may have ended with a & followed by a tab
434 * or newline etc. which we don't want to swallow
437 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
438 int start_count
, const WCHAR
*start_str
,
439 int max
, int n
, int prefix
)
441 /* This is specific to wide characters, MSDN doesn't say anything much
442 * about Unicode surrogates yet and it isn't clear if _wcsinc will
443 * correctly handle them so we'll just do this the easy way for now
448 const WCHAR
*str_on_entry
= start_str
;
452 if (*start_str
++ == PREFIX
&& max
--)
455 start_count
-= (start_str
- str_on_entry
);
462 *new_str
= start_str
;
463 *new_count
= start_count
;
466 /*********************************************************************
469 * Reanalyse the text to find the prefixed character. This is called when
470 * wordbreaking or ellipsification has shortened the string such that the
471 * previously noted prefixed character is no longer visible.
474 * str [in] The original string segment (including all characters)
475 * n1 [in] The number of characters visible before the path ellipsis
476 * n2 [in] The number of characters replaced by the path ellipsis
477 * ne [in] The number of characters in the path ellipsis, ignored if
479 * n3 [in] The number of characters visible after the path ellipsis
482 * The prefix offset within the new string segment (the one that contains the
483 * ellipses and does not contain the prefix characters) (-1 if none)
486 * We know that n1+n2+n3 must be strictly less than the length of the segment
487 * (because otherwise there would be no need to call this function)
490 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int n1
, unsigned int n2
,
491 unsigned int ne
, unsigned int n3
)
495 unsigned int n
= n1
+ n2
+ n3
;
501 /* Reached the path ellipsis; jump over it */
504 if (!n3
) break; /* Nothing after the path ellipsis */
506 if (*str
++ == PREFIX
)
508 result
= (i
< n1
) ? i
: i
- n2
+ ne
;
517 /*********************************************************************
518 * Returns true if and only if the remainder of the line is a single
519 * newline representation or nothing
522 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
524 if (!num_chars
) return TRUE
;
525 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
526 if (!--num_chars
) return TRUE
;
527 if (*str
== *(str
+1)) return FALSE
;
529 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
530 if (--num_chars
) return FALSE
;
534 /*********************************************************************
535 * Return next line of text from a string.
537 * hdc - handle to DC.
538 * str - string to parse into lines.
539 * count - length of str.
540 * dest - destination in which to return line.
541 * len - dest buffer size in chars on input, copied length into dest on output.
542 * width - maximum width of line in pixels.
543 * format - format type passed to DrawText.
544 * retsize - returned size of the line in pixels.
545 * last_line - TRUE if is the last line that will be processed
546 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
547 * the return string is built.
549 * Returns pointer to next char in str after end of the line
550 * or NULL if end of str reached.
552 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
553 WCHAR
*dest
, int *len
, int width
, DWORD format
,
554 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
)
560 int seg_i
, seg_count
, seg_j
;
568 /* For each text segment in the line */
574 /* Skip any leading tabs */
576 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
578 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
579 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
580 while (*count
&& str
[i
] == TAB
)
583 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
588 /* Now copy as far as the next tab or cr/lf or eos */
595 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
596 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
598 if (str
[i
] == PREFIX
&& !(format
& DT_NOPREFIX
) && *count
> 1)
600 (*count
)--, i
++; /* Throw away the prefix itself */
601 if (str
[i
] == PREFIX
)
603 /* Swallow it before we see it again */
604 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
606 else if (prefix_offset
== -1 || prefix_offset
>= seg_j
)
610 /* else the previous prefix was in an earlier segment of the
611 * line; we will leave it to the drawing code to catch this
617 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
622 /* Measure the whole text segment and possibly WordBreak and
626 j_in_seg
= j
- seg_j
;
627 max_seg_width
= width
- plen
;
628 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
630 /* The Microsoft handling of various combinations of formats is weird.
631 * The following may very easily be incorrect if several formats are
632 * combined, and may differ between versions (to say nothing of the
633 * several bugs in the Microsoft versions).
636 line_fits
= (num_fit
>= j_in_seg
);
637 if (!line_fits
&& (format
& DT_WORDBREAK
))
641 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
642 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
643 line_fits
= (size
.cx
<= max_seg_width
);
644 /* and correct the counts */
645 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
646 chars_used
, !(format
& DT_NOPREFIX
));
650 len_before_ellipsis
= j_in_seg
;
651 len_under_ellipsis
= 0;
652 len_after_ellipsis
= 0;
655 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
657 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
658 max_seg_width
, &size
, *p_retstr
, &len_before_ellipsis
, &len_ellipsis
, &len_under_ellipsis
, &len_after_ellipsis
);
659 line_fits
= (size
.cx
<= max_seg_width
);
662 /* NB we may end up ellipsifying a word-broken or path_ellipsified
664 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
665 ((format
& DT_END_ELLIPSIS
) &&
666 ((last_line
&& *count
) ||
667 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
670 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
671 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
672 if (before
> len_before_ellipsis
)
674 /* We must have done a path ellipsis too */
675 len_after_ellipsis
= before
- len_before_ellipsis
- len_ellipsis
;
679 len_before_ellipsis
= before
;
680 /* len_after_ellipsis remains as zero as does
681 * len_under_ellsipsis
684 line_fits
= (size
.cx
<= max_seg_width
);
687 /* As an optimisation if we have ellipsified and we are expanding
688 * tabs and we haven't reached the end of the line we can skip to it
689 * now rather than going around the loop again.
691 if ((format
& DT_EXPANDTABS
) && ellipsified
)
693 if (format
& DT_SINGLELINE
)
697 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
704 j
= seg_j
+ j_in_seg
;
705 if (prefix_offset
>= seg_j
+ len_before_ellipsis
)
707 prefix_offset
= TEXT_Reprefix (str
+ seg_i
, len_before_ellipsis
,
708 len_under_ellipsis
, len_ellipsis
,
710 if (prefix_offset
!= -1)
711 prefix_offset
+= seg_j
;
715 if (size
.cy
> retsize
->cy
)
716 retsize
->cy
= size
.cy
;
722 else if (str
[i
] == CR
|| str
[i
] == LF
)
725 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
731 /* else it was a Tab and we go around again */
743 /***********************************************************************
744 * TEXT_DrawUnderscore
746 * Draw the underline under the prefixed character
749 * hdc [in] The handle of the DC for drawing
750 * x [in] The x location of the line segment (logical coordinates)
751 * y [in] The y location of where the underscore should appear
752 * (logical coordinates)
753 * str [in] The text of the line segment
754 * offset [in] The offset of the underscored character within str
757 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
)
765 GetTextExtentPointW (hdc
, str
, offset
, &size
);
766 prefix_x
= x
+ size
.cx
;
767 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
768 prefix_end
= x
+ size
.cx
- 1;
769 /* The above method may eventually be slightly wrong due to kerning etc. */
771 hpen
= CreatePen (PS_SOLID
, 1, GetTextColor (hdc
));
772 oldPen
= SelectObject (hdc
, hpen
);
773 MoveToEx (hdc
, prefix_x
, y
, NULL
);
774 LineTo (hdc
, prefix_end
, y
);
775 SelectObject (hdc
, oldPen
);
779 /***********************************************************************
780 * DrawTextExW (USER32.@)
782 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
783 * is not quite complete, especially with regard to \0. We will assume that
784 * the returned string could have a length of up to i_count+3 and also have
785 * a trailing \0 (which would be 4 more than a not-null-terminated string but
786 * 3 more than a null-terminated string). If this is not so then increase
787 * the allowance in DrawTextExA.
789 #define MAX_STATIC_BUFFER 1024
790 INT WINAPI
DrawTextExW( HDC hdc
, LPWSTR str
, INT i_count
,
791 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
795 WCHAR
*retstr
, *p_retstr
;
797 static WCHAR line
[MAX_STATIC_BUFFER
];
798 int len
, lh
, count
=i_count
;
800 int lmargin
= 0, rmargin
= 0;
801 int x
= rect
->left
, y
= rect
->top
;
802 int width
= rect
->right
- rect
->left
;
806 TRACE("%s, %d , [(%d,%d),(%d,%d)]\n", debugstr_wn (str
, count
), count
,
807 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
809 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
810 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
813 if (count
== -1) count
= strlenW(str
);
814 if (count
== 0) return 0;
817 if (flags
& DT_SINGLELINE
)
818 flags
&= ~DT_WORDBREAK
;
820 GetTextMetricsW(hdc
, &tm
);
821 if (flags
& DT_EXTERNALLEADING
)
822 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
828 lmargin
= dtp
->iLeftMargin
* tm
.tmAveCharWidth
;
829 rmargin
= dtp
->iRightMargin
* tm
.tmAveCharWidth
;
830 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
832 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
835 if (flags
& DT_EXPANDTABS
)
837 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
) ? dtp
->iTabLength
: 8;
838 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
841 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
843 if (flags
& DT_MODIFYSTRING
)
845 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
846 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
847 if (!retstr
) return 0;
848 memcpy (retstr
, str
, size_retstr
);
860 len
= MAX_STATIC_BUFFER
;
861 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
862 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
);
864 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
866 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
868 if (flags
& DT_SINGLELINE
)
870 if (flags
& DT_VCENTER
) y
= rect
->top
+
871 (rect
->bottom
- rect
->top
) / 2 - size
.cy
/ 2;
872 else if (flags
& DT_BOTTOM
) y
= rect
->bottom
- size
.cy
;
875 if (!(flags
& DT_CALCRECT
))
877 const WCHAR
*str
= line
;
883 if ((flags
& DT_EXPANDTABS
))
886 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
888 if (len_seg
!= len
&& !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
894 if (!ExtTextOutW( hdc
, xseg
, y
,
895 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
896 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
897 rect
, str
, len_seg
, NULL
)) return 0;
898 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
900 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
);
906 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
908 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
909 if (prefix_offset
!= -1)
911 if (prefix_offset
< len_seg
)
913 /* We have just drawn an underscore; we ought to
914 * figure out where the next one is. I am going
915 * to leave it for now until I have a better model
916 * for the line, which will make reprefixing easier
921 prefix_offset
-= len_seg
;
926 else if (size
.cx
> max_width
)
931 dtp
->uiLengthDrawn
+= len
;
933 while (strPtr
&& !last_line
);
935 if (flags
& DT_CALCRECT
)
937 rect
->right
= rect
->left
+ max_width
;
940 rect
->right
+= lmargin
+ rmargin
;
944 memcpy (str
, retstr
, size_retstr
);
945 HeapFree (GetProcessHeap(), 0, retstr
);
947 return y
- rect
->top
;
950 /***********************************************************************
951 * DrawTextExA (USER32.@)
953 * If DT_MODIFYSTRING is specified then there must be room for up to
954 * 4 extra characters. We take great care about just how much modified
957 INT WINAPI
DrawTextExA( HDC hdc
, LPSTR str
, INT count
,
958 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
968 if (count
== -1) count
= strlen(str
);
969 if (!count
) return 0;
970 wcount
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
973 if (flags
& DT_MODIFYSTRING
)
978 wstr
= HeapAlloc(GetProcessHeap(), 0, wmax
* sizeof(WCHAR
));
981 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, wcount
);
982 if (flags
& DT_MODIFYSTRING
)
983 for (i
=4, p
=wstr
+wcount
; i
--; p
++) *p
=0xFFFE;
984 /* Initialise the extra characters so that we can see which ones
985 * change. U+FFFE is guaranteed to be not a unicode character and
986 * so will not be generated by DrawTextEx itself.
988 ret
= DrawTextExW( hdc
, wstr
, wcount
, rect
, flags
, NULL
);
989 if (flags
& DT_MODIFYSTRING
)
991 /* Unfortunately the returned string may contain multiple \0s
992 * and so we need to measure it ourselves.
994 for (i
=4, p
=wstr
+wcount
; i
-- && *p
!= 0xFFFE; p
++) wcount
++;
995 WideCharToMultiByte( CP_ACP
, 0, wstr
, wcount
, str
, amax
, NULL
, NULL
);
997 HeapFree(GetProcessHeap(), 0, wstr
);
1002 /***********************************************************************
1003 * DrawTextW (USER32.@)
1005 INT WINAPI
DrawTextW( HDC hdc
, LPCWSTR str
, INT count
, LPRECT rect
, UINT flags
)
1009 memset (&dtp
, 0, sizeof(dtp
));
1010 if (flags
& DT_TABSTOP
)
1012 dtp
.iTabLength
= (flags
>> 8) && 0xff;
1013 flags
&= 0xffff00ff;
1015 return DrawTextExW(hdc
, (LPWSTR
)str
, count
, rect
, flags
, &dtp
);
1018 /***********************************************************************
1019 * DrawTextA (USER32.@)
1021 INT WINAPI
DrawTextA( HDC hdc
, LPCSTR str
, INT count
, LPRECT rect
, UINT flags
)
1025 memset (&dtp
, 0, sizeof(dtp
));
1026 if (flags
& DT_TABSTOP
)
1028 dtp
.iTabLength
= (flags
>> 8) && 0xff;
1029 flags
&= 0xffff00ff;
1031 return DrawTextExA( hdc
, (LPSTR
)str
, count
, rect
, flags
, &dtp
);
1034 /***********************************************************************
1035 * DrawText (USER.85)
1037 INT16 WINAPI
DrawText16( HDC16 hdc
, LPCSTR str
, INT16 count
, LPRECT16 rect
, UINT16 flags
)
1044 CONV_RECT16TO32( rect
, &rect32
);
1045 ret
= DrawTextA( hdc
, str
, count
, &rect32
, flags
);
1046 CONV_RECT32TO16( &rect32
, rect
);
1048 else ret
= DrawTextA( hdc
, str
, count
, NULL
, flags
);
1053 /***********************************************************************
1055 * GrayString functions
1058 /* ### start build ### */
1059 extern WORD CALLBACK
TEXT_CallTo16_word_wlw(GRAYSTRINGPROC16
,WORD
,LONG
,WORD
);
1060 /* ### stop build ### */
1062 struct gray_string_info
1064 GRAYSTRINGPROC16 proc
;
1068 /* callback for 16-bit gray string proc */
1069 static BOOL CALLBACK
gray_string_callback( HDC hdc
, LPARAM param
, INT len
)
1071 const struct gray_string_info
*info
= (struct gray_string_info
*)param
;
1072 return TEXT_CallTo16_word_wlw( info
->proc
, hdc
, info
->param
, len
);
1075 /***********************************************************************
1078 * FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
1079 * heap and we can guarantee that the handles fit in an INT16. We have to
1080 * rethink the strategy once the migration to NT handles is complete.
1081 * We are going to get a lot of code-duplication once this migration is
1085 static BOOL
TEXT_GrayString(HDC hdc
, HBRUSH hb
, GRAYSTRINGPROC fn
, LPARAM lp
, INT len
,
1086 INT x
, INT y
, INT cx
, INT cy
, BOOL unicode
, BOOL _32bit
)
1088 HBITMAP hbm
, hbmsave
;
1096 if(!hdc
) return FALSE
;
1097 if (!(memdc
= CreateCompatibleDC(hdc
))) return FALSE
;
1102 slen
= lstrlenW((LPCWSTR
)lp
);
1104 slen
= strlen((LPCSTR
)lp
);
1106 slen
= strlen(MapSL(lp
));
1109 if((cx
== 0 || cy
== 0) && slen
!= -1)
1113 GetTextExtentPoint32W(hdc
, (LPCWSTR
)lp
, slen
, &s
);
1115 GetTextExtentPoint32A(hdc
, (LPCSTR
)lp
, slen
, &s
);
1117 GetTextExtentPoint32A(hdc
, MapSL(lp
), slen
, &s
);
1118 if(cx
== 0) cx
= s
.cx
;
1119 if(cy
== 0) cy
= s
.cy
;
1122 hbm
= CreateBitmap(cx
, cy
, 1, 1, NULL
);
1123 hbmsave
= (HBITMAP
)SelectObject(memdc
, hbm
);
1124 hbsave
= SelectObject( memdc
, GetStockObject(BLACK_BRUSH
) );
1125 PatBlt( memdc
, 0, 0, cx
, cy
, PATCOPY
);
1126 SelectObject( memdc
, hbsave
);
1127 SetTextColor(memdc
, RGB(255, 255, 255));
1128 SetBkColor(memdc
, RGB(0, 0, 0));
1129 hfsave
= (HFONT
)SelectObject(memdc
, GetCurrentObject(hdc
, OBJ_FONT
));
1134 retval
= fn(memdc
, lp
, slen
);
1136 retval
= (BOOL
)((BOOL16
)((GRAYSTRINGPROC16
)fn
)((HDC16
)memdc
, lp
, (INT16
)slen
));
1141 TextOutW(memdc
, 0, 0, (LPCWSTR
)lp
, slen
);
1143 TextOutA(memdc
, 0, 0, (LPCSTR
)lp
, slen
);
1145 TextOutA(memdc
, 0, 0, MapSL(lp
), slen
);
1148 SelectObject(memdc
, hfsave
);
1151 * Windows doc says that the bitmap isn't grayed when len == -1 and
1152 * the callback function returns FALSE. However, testing this on
1153 * win95 showed otherwise...
1155 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
1156 if(retval
|| len
!= -1)
1159 hbsave
= (HBRUSH
)SelectObject(memdc
, CACHE_GetPattern55AABrush());
1160 PatBlt(memdc
, 0, 0, cx
, cy
, 0x000A0329);
1161 SelectObject(memdc
, hbsave
);
1164 if(hb
) hbsave
= (HBRUSH
)SelectObject(hdc
, hb
);
1165 fg
= SetTextColor(hdc
, RGB(0, 0, 0));
1166 bg
= SetBkColor(hdc
, RGB(255, 255, 255));
1167 BitBlt(hdc
, x
, y
, cx
, cy
, memdc
, 0, 0, 0x00E20746);
1168 SetTextColor(hdc
, fg
);
1169 SetBkColor(hdc
, bg
);
1170 if(hb
) SelectObject(hdc
, hbsave
);
1172 SelectObject(memdc
, hbmsave
);
1179 /***********************************************************************
1180 * GrayString (USER.185)
1182 BOOL16 WINAPI
GrayString16( HDC16 hdc
, HBRUSH16 hbr
, GRAYSTRINGPROC16 gsprc
,
1183 LPARAM lParam
, INT16 cch
, INT16 x
, INT16 y
,
1184 INT16 cx
, INT16 cy
)
1186 struct gray_string_info info
;
1188 if (!gsprc
) return TEXT_GrayString(hdc
, hbr
, NULL
, lParam
, cch
, x
, y
, cx
, cy
, FALSE
, FALSE
);
1190 info
.param
= lParam
;
1191 return TEXT_GrayString( hdc
, hbr
, gray_string_callback
, (LPARAM
)&info
,
1192 cch
, x
, y
, cx
, cy
, FALSE
, FALSE
);
1196 /***********************************************************************
1197 * GrayStringA (USER32.@)
1199 BOOL WINAPI
GrayStringA( HDC hdc
, HBRUSH hbr
, GRAYSTRINGPROC gsprc
,
1200 LPARAM lParam
, INT cch
, INT x
, INT y
,
1203 return TEXT_GrayString(hdc
, hbr
, gsprc
, lParam
, cch
, x
, y
, cx
, cy
,
1208 /***********************************************************************
1209 * GrayStringW (USER32.@)
1211 BOOL WINAPI
GrayStringW( HDC hdc
, HBRUSH hbr
, GRAYSTRINGPROC gsprc
,
1212 LPARAM lParam
, INT cch
, INT x
, INT y
,
1215 return TEXT_GrayString(hdc
, hbr
, gsprc
, lParam
, cch
, x
, y
, cx
, cy
,
1219 /***********************************************************************
1221 * TabbedText functions
1224 /***********************************************************************
1225 * TEXT_TabbedTextOut
1227 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
1228 * Note: this doesn't work too well for text-alignment modes other
1229 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
1231 static LONG
TEXT_TabbedTextOut( HDC hdc
, INT x
, INT y
, LPCSTR lpstr
,
1232 INT count
, INT cTabStops
, const INT16
*lpTabPos16
,
1233 const INT
*lpTabPos32
, INT nTabOrg
,
1246 defWidth
= lpTabPos32
? *lpTabPos32
: *lpTabPos16
;
1252 GetTextMetricsA( hdc
, &tm
);
1253 defWidth
= 8 * tm
.tmAveCharWidth
;
1258 for (i
= 0; i
< count
; i
++)
1259 if (lpstr
[i
] == '\t') break;
1260 GetTextExtentPointA( hdc
, lpstr
, i
, &extent
);
1263 while ((cTabStops
> 0) &&
1264 (nTabOrg
+ *lpTabPos32
<= x
+ extent
.cx
))
1272 while ((cTabStops
> 0) &&
1273 (nTabOrg
+ *lpTabPos16
<= x
+ extent
.cx
))
1280 tabPos
= x
+ extent
.cx
;
1281 else if (cTabStops
> 0)
1282 tabPos
= nTabOrg
+ (lpTabPos32
? *lpTabPos32
: *lpTabPos16
);
1284 tabPos
= nTabOrg
+ ((x
+ extent
.cx
- nTabOrg
) / defWidth
+ 1) * defWidth
;
1291 r
.bottom
= y
+ extent
.cy
;
1292 ExtTextOutA( hdc
, x
, y
,
1293 GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
1294 &r
, lpstr
, i
, NULL
);
1300 return MAKELONG(tabPos
- start
, extent
.cy
);
1304 /***********************************************************************
1305 * TabbedTextOut (USER.196)
1307 LONG WINAPI
TabbedTextOut16( HDC16 hdc
, INT16 x
, INT16 y
, LPCSTR lpstr
,
1308 INT16 count
, INT16 cTabStops
,
1309 const INT16
*lpTabPos
, INT16 nTabOrg
)
1311 TRACE("%04x %d,%d %s %d\n", hdc
, x
, y
, debugstr_an(lpstr
,count
), count
);
1312 return TEXT_TabbedTextOut( hdc
, x
, y
, lpstr
, count
, cTabStops
,
1313 lpTabPos
, NULL
, nTabOrg
, TRUE
);
1317 /***********************************************************************
1318 * TabbedTextOutA (USER32.@)
1320 LONG WINAPI
TabbedTextOutA( HDC hdc
, INT x
, INT y
, LPCSTR lpstr
, INT count
,
1321 INT cTabStops
, const INT
*lpTabPos
, INT nTabOrg
)
1323 TRACE("%04x %d,%d %s %d\n", hdc
, x
, y
, debugstr_an(lpstr
,count
), count
);
1324 return TEXT_TabbedTextOut( hdc
, x
, y
, lpstr
, count
, cTabStops
,
1325 NULL
, lpTabPos
, nTabOrg
, TRUE
);
1329 /***********************************************************************
1330 * TabbedTextOutW (USER32.@)
1332 LONG WINAPI
TabbedTextOutW( HDC hdc
, INT x
, INT y
, LPCWSTR str
, INT count
,
1333 INT cTabStops
, const INT
*lpTabPos
, INT nTabOrg
)
1338 UINT codepage
= CP_ACP
; /* FIXME: get codepage of font charset */
1340 acount
= WideCharToMultiByte(codepage
,0,str
,count
,NULL
,0,NULL
,NULL
);
1341 p
= HeapAlloc( GetProcessHeap(), 0, acount
);
1342 if(p
== NULL
) return 0; /* FIXME: is this the correct return on failure */
1343 acount
= WideCharToMultiByte(codepage
,0,str
,count
,p
,acount
,NULL
,NULL
);
1344 ret
= TabbedTextOutA( hdc
, x
, y
, p
, acount
, cTabStops
, lpTabPos
, nTabOrg
);
1345 HeapFree( GetProcessHeap(), 0, p
);
1350 /***********************************************************************
1351 * GetTabbedTextExtent (USER.197)
1353 DWORD WINAPI
GetTabbedTextExtent16( HDC16 hdc
, LPCSTR lpstr
, INT16 count
,
1354 INT16 cTabStops
, const INT16
*lpTabPos
)
1356 TRACE("%04x %s %d\n", hdc
, debugstr_an(lpstr
,count
), count
);
1357 return TEXT_TabbedTextOut( hdc
, 0, 0, lpstr
, count
, cTabStops
,
1358 lpTabPos
, NULL
, 0, FALSE
);
1362 /***********************************************************************
1363 * GetTabbedTextExtentA (USER32.@)
1365 DWORD WINAPI
GetTabbedTextExtentA( HDC hdc
, LPCSTR lpstr
, INT count
,
1366 INT cTabStops
, const INT
*lpTabPos
)
1368 TRACE("%04x %s %d\n", hdc
, debugstr_an(lpstr
,count
), count
);
1369 return TEXT_TabbedTextOut( hdc
, 0, 0, lpstr
, count
, cTabStops
,
1370 NULL
, lpTabPos
, 0, FALSE
);
1374 /***********************************************************************
1375 * GetTabbedTextExtentW (USER32.@)
1377 DWORD WINAPI
GetTabbedTextExtentW( HDC hdc
, LPCWSTR lpstr
, INT count
,
1378 INT cTabStops
, const INT
*lpTabPos
)
1383 UINT codepage
= CP_ACP
; /* FIXME: get codepage of font charset */
1385 acount
= WideCharToMultiByte(codepage
,0,lpstr
,count
,NULL
,0,NULL
,NULL
);
1386 p
= HeapAlloc( GetProcessHeap(), 0, acount
);
1387 if(p
== NULL
) return 0; /* FIXME: is this the correct failure value? */
1388 acount
= WideCharToMultiByte(codepage
,0,lpstr
,count
,p
,acount
,NULL
,NULL
);
1389 ret
= GetTabbedTextExtentA( hdc
, p
, acount
, cTabStops
, lpTabPos
);
1390 HeapFree( GetProcessHeap(), 0, p
);