3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * PROJECT: ReactOS user32.dll
21 * FILE: win32ss/user/rtl/text.c
23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * 09-05-2001 CSH Created
28 /* INCLUDES ******************************************************************/
32 DBG_DEFAULT_CHANNEL(UserMenu
);
35 #include <wine/debug.h>
36 WINE_DEFAULT_DEBUG_CHANNEL(text
);
39 /* FUNCTIONS *****************************************************************/
47 #define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__))
54 void _font_assert(const char *msg
, const char *file
, int line
)
56 /* Assertion failed at foo.c line 45: x<y */
57 DbgPrint("Assertion failed at %s line %d: %s\n", file
, line
, msg
);
62 for(;;); /* eliminate warning by mingw */
66 /*********************************************************************
71 * Copyright 1993, 1994 Alexandre Julliard
72 * Copyright 2002 Bill Medland
75 * How many buffers to use
76 * While processing in DrawText there are potentially three different forms
77 * of the text that need to be held. How are they best held?
78 * 1. The original text is needed, of course, to see what to display.
79 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
81 * 3. The buffered text that is about to be displayed e.g. the current line.
82 * Typically this will exclude the ampersands used for prefixing etc.
85 * a. If the buffered text to be displayed includes the ampersands then
86 * we will need special measurement and draw functions that will ignore
87 * the ampersands (e.g. by copying to a buffer without the prefix and
88 * then using the normal forms). This may involve less space but may
89 * require more processing. e.g. since a line containing tabs may
90 * contain several underlined characters either we need to carry around
91 * a list of prefix locations or we may need to locate them several
93 * b. If we actually directly modify the "original text" as we go then we
94 * will need some special "caching" to handle the fact that when we
95 * ellipsify the text the ellipsis may modify the next line of text,
96 * which we have not yet processed. (e.g. ellipsification of a W at the
97 * end of a line will overwrite the W, the \n and the first character of
98 * the next line, and a \0 will overwrite the second. Try it!!)
100 * Option 1. Three separate storages. (To be implemented)
101 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
102 * the edited string in some form, either as the string itself or as some
103 * sort of "edit list" to be applied just before returning.
104 * Use a buffer that holds the ellipsified current line sans ampersands
105 * and accept the need occasionally to recalculate the prefixes (if
106 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
114 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
115 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
117 #define FORWARD_SLASH '/'
118 #define BACK_SLASH '\\'
120 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
122 typedef struct tag_ellipsis_data
130 /*********************************************************************
131 * TEXT_Ellipsify (static)
133 * Add an ellipsis to the end of the given string whilst ensuring it fits.
135 * If the ellipsis alone doesn't fit then it will be returned anyway.
137 * See Also TEXT_PathEllipsify
140 * hdc [in] The handle to the DC that defines the font.
141 * str [in/out] The string that needs to be modified.
142 * max_str [in] The dimension of str (number of WCHAR).
143 * len_str [in/out] The number of characters in str
144 * width [in] The maximum width permitted (in logical coordinates)
145 * size [out] The dimensions of the text
146 * modstr [out] The modified form of the string, to be returned to the
147 * calling program. It is assumed that the caller has
148 * made sufficient space available so we don't need to
149 * know the size of the space. This pointer may be NULL if
150 * the modified string is not required.
151 * len_before [out] The number of characters before the ellipsis.
152 * len_ellip [out] The number of characters in the ellipsis.
154 * See for example Microsoft article Q249678.
156 * For now we will simply use three dots rather than worrying about whether
157 * the font contains an explicit ellipsis character.
159 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
160 unsigned int *len_str
, int width
, SIZE
*size
,
162 int *len_before
, int *len_ellip
)
164 unsigned int len_ellipsis
;
165 unsigned int lo
, mid
, hi
;
166 len_ellipsis
= strlenW (ELLIPSISW
);
167 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
168 if (*len_str
> max_len
- len_ellipsis
)
169 *len_str
= max_len
- len_ellipsis
;
171 /* First do a quick binary search to get an upper bound for *len_str. */
174 GreGetTextExtentExW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
, 0) &&
176 GetTextExtentExPointW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
) &&
180 for (lo
= 0, hi
= *len_str
; lo
< hi
; )
184 if (!GreGetTextExtentExW(hdc
, str
, mid
, width
, NULL
, NULL
, size
, 0))
186 if (!GetTextExtentExPointW(hdc
, str
, mid
, width
, NULL
, NULL
, size
))
189 if (size
->cx
> width
)
196 /* Now this should take only a couple iterations at most. */
199 memcpy(str
+ *len_str
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
201 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
202 NULL
, NULL
, size
, 0)) break;
204 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
205 NULL
, NULL
, size
)) break;
207 if (!*len_str
|| size
->cx
<= width
) break;
211 *len_ellip
= len_ellipsis
;
212 *len_before
= *len_str
;
213 *len_str
+= len_ellipsis
;
217 memcpy (modstr
, str
, *len_str
* sizeof(WCHAR
));
218 modstr
[*len_str
] = '\0';
222 /*********************************************************************
223 * TEXT_PathEllipsify (static)
225 * Add an ellipsis to the provided string in order to make it fit within
226 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
229 * See Also TEXT_Ellipsify
232 * hdc [in] The handle to the DC that defines the font.
233 * str [in/out] The string that needs to be modified
234 * max_str [in] The dimension of str (number of WCHAR).
235 * len_str [in/out] The number of characters in str
236 * width [in] The maximum width permitted (in logical coordinates)
237 * size [out] The dimensions of the text
238 * modstr [out] The modified form of the string, to be returned to the
239 * calling program. It is assumed that the caller has
240 * made sufficient space available so we don't need to
241 * know the size of the space. This pointer may be NULL if
242 * the modified string is not required.
243 * pellip [out] The ellipsification results
245 * For now we will simply use three dots rather than worrying about whether
246 * the font contains an explicit ellipsis character.
248 * The following applies, I think to Win95. We will need to extend it for
249 * Win98 which can have both path and end ellipsis at the same time (e.g.
250 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
252 * The resulting string consists of as much as possible of the following:
253 * 1. The ellipsis itself
254 * 2. The last \ or / of the string (if any)
255 * 3. Everything after the last \ or / of the string (if any) or the whole
256 * string if there is no / or \. I believe that under Win95 this would
257 * include everything even though some might be clipped off the end whereas
258 * under Win98 that might be ellipsified too.
259 * Yet to be investigated is whether this would include wordbreaking if the
260 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
261 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
262 * broken within words).
263 * 4. All the stuff before the / or \, which is placed before the ellipsis.
265 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
266 unsigned int *len_str
, int width
, SIZE
*size
,
267 WCHAR
*modstr
, ellipsis_data
*pellip
)
272 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
273 len_ellipsis
= strlenW (ELLIPSISW
);
274 if (!max_len
) return;
275 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
276 if (*len_str
+ len_ellipsis
>= max_len
)
277 *len_str
= max_len
- len_ellipsis
-1;
278 /* Hopefully this will never happen, otherwise it would probably lose
279 * the wrong character
281 str
[*len_str
] = '\0'; /* to simplify things */
283 lastBkSlash
= wcsrchr (str
, BACK_SLASH
);
284 lastFwdSlash
= wcsrchr (str
, FORWARD_SLASH
);
286 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
287 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
289 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
290 if (!lastSlash
) lastSlash
= str
;
291 len_trailing
= *len_str
- (lastSlash
- str
);
293 /* overlap-safe movement to the right */
294 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
295 memcpy (lastSlash
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
296 len_trailing
+= len_ellipsis
;
297 /* From this point on lastSlash actually points to the ellipsis in front
298 * of the last slash and len_trailing includes the ellipsis
305 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
306 NULL
, NULL
, size
, 0)) break;
308 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
309 NULL
, NULL
, size
)) break;
311 if (lastSlash
== str
|| size
->cx
<= width
) break;
313 /* overlap-safe movement to the left */
314 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
321 pellip
->before
= lastSlash
-str
;
322 pellip
->len
= len_ellipsis
;
323 pellip
->under
= len_under
;
324 pellip
->after
= len_trailing
- len_ellipsis
;
325 *len_str
+= len_ellipsis
;
329 memcpy(modstr
, str
, *len_str
* sizeof(WCHAR
));
330 modstr
[*len_str
] = '\0';
334 /* Check the character is Chinese, Japanese, Korean and/or Thai */
335 inline BOOL
IsCJKT(WCHAR wch
)
337 if (0x0E00 <= wch
&& wch
<= 0x0E7F)
338 return TRUE
; /* Thai */
340 if (0x3000 <= wch
&& wch
<= 0x9FFF)
341 return TRUE
; /* CJK */
343 if (0xAC00 <= wch
&& wch
<= 0xD7FF)
344 return TRUE
; /* Korean */
346 if (0xFF00 <= wch
&& wch
<= 0xFFEF)
347 return TRUE
; /* CJK */
352 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
353 static const WCHAR KinsokuClassA
[] =
355 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
356 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
357 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
358 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
359 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
360 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
361 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
362 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
363 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
366 /*********************************************************************
367 * TEXT_WordBreak (static)
369 * Perform wordbreak processing on the given string
371 * Assumes that DT_WORDBREAK has been specified and not all the characters
372 * fit. Note that this function should even be called when the first character
373 * that doesn't fit is known to be a space or tab, so that it can swallow them.
375 * Note that the Windows processing has some strange properties.
376 * 1. If the text is left-justified and there is room for some of the spaces
377 * that follow the last word on the line then those that fit are included on
379 * 2. If the text is centred or right-justified and there is room for some of
380 * the spaces that follow the last word on the line then all but one of those
381 * that fit are included on the line.
382 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
383 * character of a new line it will be skipped.
386 * hdc [in] The handle to the DC that defines the font.
387 * str [in/out] The string that needs to be broken.
388 * max_str [in] The dimension of str (number of WCHAR).
389 * len_str [in/out] The number of characters in str
390 * width [in] The maximum width permitted
391 * format [in] The format flags in effect
392 * chars_fit [in] The maximum number of characters of str that are already
393 * known to fit; chars_fit+1 is known not to fit.
394 * chars_used [out] The number of characters of str that have been "used" and
395 * do not need to be included in later text. For example this will
396 * include any spaces that have been discarded from the start of
398 * size [out] The size of the returned text in logical coordinates
400 * Pedantic assumption - Assumes that the text length is monotonically
401 * increasing with number of characters (i.e. no weird kernings)
405 * Work back from the last character that did fit to either a space or the last
406 * character of a word, whichever is met first.
407 * If there was one or the first character didn't fit then
408 * If the text is centred or right justified and that one character was a
409 * space then break the line before that character
410 * Otherwise break the line after that character
411 * and if the next character is a space then discard it.
412 * Suppose there was none (and the first character did fit).
413 * If Break Within Word is permitted
414 * break the word after the last character that fits (there must be
415 * at least one; none is caught earlier).
417 * discard any trailing space.
418 * include the whole word; it may be ellipsified later
420 * Break Within Word is permitted under a set of circumstances that are not
421 * totally clear yet. Currently our best guess is:
422 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
423 * DT_PATH_ELLIPSIS is
426 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
427 unsigned int *len_str
,
428 int width
, int format
, unsigned int chars_fit
,
429 unsigned int *chars_used
, SIZE
*size
)
433 assert (format
& DT_WORDBREAK
);
434 assert (chars_fit
< *len_str
);
436 /* Work back from the last character that did fit to either a space or the
437 * last character of a word, whichever is met first.
439 p
= str
+ chars_fit
; /* The character that doesn't fit */
443 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
444 p
--; /* the word just fitted */
447 while (p
> str
&& *(--p
) != SPACE
&& (!IsCJKT(p
[1]) ||
448 p
[1] == L
'\0' || wcschr(KinsokuClassA
, p
[1]) != NULL
))
450 word_fits
= (p
!= str
|| *p
== SPACE
|| IsCJKT(p
[1]));
452 /* If there was one. */
456 /* break the line before/after that character */
457 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
459 next_is_space
= (unsigned int) (p
- str
) < *len_str
&& *p
== SPACE
;
461 /* and if the next character is a space then discard it. */
462 *chars_used
= *len_str
;
466 /* Suppose there was none. */
469 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
472 /* break the word after the last character that fits (there must be
476 *len_str
= chars_fit
;
477 *chars_used
= chars_fit
;
479 /* FIXME - possible error. Since the next character is now removed
480 * this could make the text longer so that it no longer fits, and
481 * so we need a loop to test and shrink.
487 /* discard any trailing space. */
488 const WCHAR
*e
= str
+ *len_str
;
490 while (p
< e
&& *p
!= SPACE
)
492 *chars_used
= p
- str
;
493 if (p
< e
) /* i.e. loop failed because *p == SPACE */
496 /* include the whole word; it may be ellipsified later */
498 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
499 * so that it will be too long
503 /* Remeasure the string */
505 GreGetTextExtentExW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
, 0);
507 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
511 /*********************************************************************
514 * Skip over the given number of characters, bearing in mind prefix
515 * substitution and the fact that a character may take more than one
516 * WCHAR (Unicode surrogates are two words long) (and there may have been
520 * new_count [out] The updated count
521 * new_str [out] The updated pointer
522 * start_count [in] The count of remaining characters corresponding to the
523 * start of the string
524 * start_str [in] The starting point of the string
525 * max [in] The number of characters actually in this segment of the
526 * string (the & counts)
527 * n [in] The number of characters to skip (if prefix then
529 * prefix [in] Apply prefix substitution
535 * There must be at least n characters in the string
536 * We need max because the "line" may have ended with a & followed by a tab
537 * or newline etc. which we don't want to swallow
540 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
541 int start_count
, const WCHAR
*start_str
,
542 int max
, int n
, int prefix
)
544 /* This is specific to wide characters, MSDN doesn't say anything much
545 * about Unicode surrogates yet and it isn't clear if _wcsinc will
546 * correctly handle them so we'll just do this the easy way for now
551 const WCHAR
*str_on_entry
= start_str
;
556 if ((*start_str
== PREFIX
|| *start_str
== ALPHA_PREFIX
) && max
--)
560 start_count
-= (start_str
- str_on_entry
);
567 *new_str
= start_str
;
568 *new_count
= start_count
;
571 /*********************************************************************
574 * Reanalyse the text to find the prefixed character. This is called when
575 * wordbreaking or ellipsification has shortened the string such that the
576 * previously noted prefixed character is no longer visible.
579 * str [in] The original string segment (including all characters)
580 * ns [in] The number of characters in str (including prefixes)
581 * pe [in] The ellipsification data
584 * The prefix offset within the new string segment (the one that contains the
585 * ellipses and does not contain the prefix characters) (-1 if none)
588 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int ns
,
589 const ellipsis_data
*pe
)
593 unsigned int n
= pe
->before
+ pe
->under
+ pe
->after
;
595 for (i
= 0; i
< n
; i
++, str
++)
597 if (i
== (unsigned int) pe
->before
)
599 /* Reached the path ellipsis; jump over it */
600 if (ns
< (unsigned int) pe
->under
) break;
604 if (!pe
->after
) break; /* Nothing after the path ellipsis */
608 if (*str
++ == PREFIX
|| *str
== ALPHA_PREFIX
)
613 result
= (i
< (unsigned int) pe
->before
|| pe
->under
== 0) ? i
: i
- pe
->under
+ pe
->len
;
614 /* pe->len may be non-zero while pe_under is zero */
621 /*********************************************************************
622 * Returns true if and only if the remainder of the line is a single
623 * newline representation or nothing
626 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
628 if (!num_chars
) return TRUE
;
629 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
630 if (!--num_chars
) return TRUE
;
631 if (*str
== *(str
+1)) return FALSE
;
633 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
634 if (--num_chars
) return FALSE
;
638 /*********************************************************************
639 * Return next line of text from a string.
641 * hdc - handle to DC.
642 * str - string to parse into lines.
643 * count - length of str.
644 * dest - destination in which to return line.
645 * len - dest buffer size in chars on input, copied length into dest on output.
646 * width - maximum width of line in pixels.
647 * format - format type passed to DrawText.
648 * retsize - returned size of the line in pixels.
649 * last_line - TRUE if is the last line that will be processed
650 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
651 * the return string is built.
652 * tabwidth - The width of a tab in logical coordinates
653 * pprefix_offset - Here is where we return the offset within dest of the first
654 * prefixed (underlined) character. -1 is returned if there
655 * are none. Note that there may be more; the calling code
656 * will need to use TEXT_Reprefix to find any later ones.
657 * pellip - Here is where we return the information about any ellipsification
658 * that was carried out. Note that if tabs are being expanded then
659 * this data will correspond to the last text segment actually
660 * returned in dest; by definition there would not have been any
661 * ellipsification in earlier text segments of the line.
663 * Returns pointer to next char in str after end of the line
664 * or NULL if end of str reached.
666 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
667 WCHAR
*dest
, int *len
, int width
, DWORD format
,
668 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
,
669 int tabwidth
, int *pprefix_offset
,
670 ellipsis_data
*pellip
)
676 int seg_i
, seg_count
, seg_j
;
681 unsigned int j_in_seg
;
683 *pprefix_offset
= -1;
685 /* For each text segment in the line */
691 /* Skip any leading tabs */
693 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
695 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
696 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
697 while (*count
&& str
[i
] == TAB
)
700 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
705 /* Now copy as far as the next tab or cr/lf or eos */
712 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
713 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
715 if ((format
& DT_NOPREFIX
) || *count
<= 1)
717 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
721 if (str
[i
] == PREFIX
|| str
[i
] == ALPHA_PREFIX
) {
722 (*count
)--, i
++; /* Throw away the prefix itself */
723 if (str
[i
] == PREFIX
)
725 /* Swallow it before we see it again */
726 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
728 else if (*pprefix_offset
== -1 || *pprefix_offset
>= seg_j
)
732 /* else the previous prefix was in an earlier segment of the
733 * line; we will leave it to the drawing code to catch this
737 else if (str
[i
] == KANA_PREFIX
)
739 /* Throw away katakana access keys */
740 (*count
)--, i
++; /* skip the prefix */
741 (*count
)--, i
++; /* skip the letter */
745 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
750 /* Measure the whole text segment and possibly WordBreak and
754 j_in_seg
= j
- seg_j
;
755 max_seg_width
= width
- plen
;
757 GreGetTextExtentExW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, (PULONG
)&num_fit
, NULL
, &size
, 0);
759 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
762 /* The Microsoft handling of various combinations of formats is weird.
763 * The following may very easily be incorrect if several formats are
764 * combined, and may differ between versions (to say nothing of the
765 * several bugs in the Microsoft versions).
768 line_fits
= (num_fit
>= j_in_seg
);
769 if (!line_fits
&& (format
& DT_WORDBREAK
))
772 unsigned int chars_used
;
773 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
774 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
775 line_fits
= (size
.cx
<= max_seg_width
);
776 /* and correct the counts */
777 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
778 chars_used
, !(format
& DT_NOPREFIX
));
782 pellip
->before
= j_in_seg
;
787 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
789 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
790 max_seg_width
, &size
, *p_retstr
, pellip
);
791 line_fits
= (size
.cx
<= max_seg_width
);
794 /* NB we may end up ellipsifying a word-broken or path_ellipsified
796 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
797 ((format
& DT_END_ELLIPSIS
) &&
798 ((last_line
&& *count
) ||
799 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
801 int before
, len_ellipsis
;
802 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
803 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
804 if (before
> pellip
->before
)
806 /* We must have done a path ellipsis too */
807 pellip
->after
= before
- pellip
->before
- pellip
->len
;
808 /* Leave the len as the length of the first ellipsis */
812 /* If we are here after a path ellipsification it must be
813 * because even the ellipsis itself didn't fit.
815 assert (pellip
->under
== 0 && pellip
->after
== 0);
816 pellip
->before
= before
;
817 pellip
->len
= len_ellipsis
;
818 /* pellip->after remains as zero as does
822 line_fits
= (size
.cx
<= max_seg_width
);
825 /* As an optimisation if we have ellipsified and we are expanding
826 * tabs and we haven't reached the end of the line we can skip to it
827 * now rather than going around the loop again.
829 if ((format
& DT_EXPANDTABS
) && ellipsified
)
831 if (format
& DT_SINGLELINE
)
835 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
842 j
= seg_j
+ j_in_seg
;
843 if (*pprefix_offset
>= seg_j
+ pellip
->before
)
845 *pprefix_offset
= TEXT_Reprefix (str
+ seg_i
, i
- seg_i
, pellip
);
846 if (*pprefix_offset
!= -1)
847 *pprefix_offset
+= seg_j
;
851 if (size
.cy
> retsize
->cy
)
852 retsize
->cy
= size
.cy
;
858 else if (str
[i
] == CR
|| str
[i
] == LF
)
861 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
867 /* else it was a Tab and we go around again */
879 /***********************************************************************
880 * TEXT_DrawUnderscore
882 * Draw the underline under the prefixed character
885 * hdc [in] The handle of the DC for drawing
886 * x [in] The x location of the line segment (logical coordinates)
887 * y [in] The y location of where the underscore should appear
888 * (logical coordinates)
889 * str [in] The text of the line segment
890 * offset [in] The offset of the underscored character within str
891 * rect [in] Clipping rectangle (if not NULL)
893 /* Synced with wine 1.1.32 */
894 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
, const RECT
*rect
)
902 GreGetTextExtentW (hdc
, str
, offset
, &size
, 0);
904 GetTextExtentPointW (hdc
, str
, offset
, &size
);
906 prefix_x
= x
+ size
.cx
;
908 GreGetTextExtentW (hdc
, str
, offset
+1, &size
, 0);
910 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
912 prefix_end
= x
+ size
.cx
- 1;
913 /* The above method may eventually be slightly wrong due to kerning etc. */
915 /* Check for clipping */
918 if (prefix_x
> rect
->right
|| prefix_end
< rect
->left
||
919 y
< rect
->top
|| y
> rect
->bottom
)
920 return; /* Completely outside */
921 /* Partially outside */
922 if (prefix_x
< rect
->left
) prefix_x
= rect
->left
;
923 if (prefix_end
> rect
->right
) prefix_end
= rect
->right
;
926 hpen
= NtGdiCreatePen (PS_SOLID
, 1, GreGetTextColor (hdc
), NULL
);
927 oldPen
= NtGdiSelectPen (hdc
, hpen
);
928 GreMoveTo (hdc
, prefix_x
, y
, NULL
);
929 NtGdiLineTo (hdc
, prefix_end
, y
);
930 NtGdiSelectPen (hdc
, oldPen
);
931 GreDeleteObject (hpen
);
933 hpen
= CreatePen (PS_SOLID
, 1, GetTextColor (hdc
));
934 oldPen
= SelectObject (hdc
, hpen
);
935 MoveToEx (hdc
, prefix_x
, y
, NULL
);
936 LineTo (hdc
, prefix_end
, y
);
937 SelectObject (hdc
, oldPen
);
942 /***********************************************************************
943 * DrawTextExW (USER32.@)
945 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
946 * is not quite complete, especially with regard to \0. We will assume that
947 * the returned string could have a length of up to i_count+3 and also have
948 * a trailing \0 (which would be 4 more than a not-null-terminated string but
949 * 3 more than a null-terminated string). If this is not so then increase
950 * the allowance in DrawTextExA.
952 #define MAX_BUFFER 1024
956 * Synced with Wine Staging 1.7.37
958 INT WINAPI
DrawTextExWorker( HDC hdc
,
963 LPDRAWTEXTPARAMS dtp
)
967 WCHAR
*retstr
, *p_retstr
;
969 WCHAR line
[MAX_BUFFER
];
970 int len
, lh
, count
=i_count
;
972 int lmargin
= 0, rmargin
= 0;
973 int x
= rect
->left
, y
= rect
->top
;
974 int width
= rect
->right
- rect
->left
;
977 int tabwidth
/* to keep gcc happy */ = 0;
985 TRACE("%S, %d, %08x\n", str
, count
, flags
);
987 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str
, count
), count
,
988 wine_dbgstr_rect(rect
), flags
);
990 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
991 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
997 if (flags
& DT_SINGLELINE
)
998 flags
&= ~DT_WORDBREAK
;
1000 GreGetTextMetricsW(hdc
, &tm
);
1002 GetTextMetricsW(hdc
, &tm
);
1004 if (flags
& DT_EXTERNALLEADING
)
1005 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1009 if (str
[0] && count
== 0)
1012 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1015 if (GreGetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1017 SIZE window_ext
, viewport_ext
;
1018 GreGetWindowExtEx(hdc
, &window_ext
);
1019 GreGetViewportExtEx(hdc
, &viewport_ext
);
1020 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1024 if (GetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1026 SIZE window_ext
, viewport_ext
;
1027 GetWindowExtEx(hdc
, &window_ext
);
1028 GetViewportExtEx(hdc
, &viewport_ext
);
1029 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1036 count
= wcslen(str
);
1038 count
= strlenW(str
);
1042 if( flags
& DT_CALCRECT
)
1044 rect
->right
= rect
->left
;
1045 if( flags
& DT_SINGLELINE
)
1046 rect
->bottom
= rect
->top
+ (invert_y
? -lh
: lh
);
1048 rect
->bottom
= rect
->top
;
1056 lmargin
= dtp
->iLeftMargin
;
1057 rmargin
= dtp
->iRightMargin
;
1058 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
1060 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
1063 if (flags
& DT_EXPANDTABS
)
1065 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
&& dtp
->iTabLength
) ? dtp
->iTabLength
: 8;
1066 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
1069 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
1070 #ifndef _WIN32K_ ///// Fix CORE-2201.
1071 if (!(flags
& DT_NOCLIP
) )
1074 hrgn
= CreateRectRgn(0,0,0,0);
1077 hasClip
= GetClipRgn(hdc
, hrgn
);
1078 // If the region to be retrieved is NULL, the return value is 0.
1084 IntersectClipRect(hdc
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
1088 if (!(flags
& DT_NOCLIP
) )
1091 hrgn
= NtGdiCreateRectRgn(0,0,0,0);
1094 hasClip
= NtGdiGetRandomRgn(hdc
, hrgn
, CLIPRGN
);
1097 GreDeleteObject(hrgn
);
1100 NtGdiIntersectClipRect(hdc
, rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
1104 if (flags
& DT_MODIFYSTRING
)
1106 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
1108 retstr
= ExAllocatePoolWithTag(PagedPool
, size_retstr
, USERTAG_RTL
);
1110 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
1112 if (!retstr
) return 0;
1113 memcpy (retstr
, str
, size_retstr
);
1124 len
= sizeof(line
)/sizeof(line
[0]);
1126 last_line
= !(flags
& DT_NOCLIP
) && y
- ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) < rect
->bottom
;
1128 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
1129 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
, tabwidth
, &prefix_offset
, &ellip
);
1132 if (flags
& DT_CENTER
)
1134 if (((rect
->right
- rect
->left
) < size
.cx
) && (flags
& DT_CALCRECT
))
1136 x
= rect
->left
+ size
.cx
;
1140 x
= (rect
->left
+ rect
->right
- size
.cx
) / 2;
1144 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
1147 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
1149 if (flags
& DT_SINGLELINE
)
1152 if (flags
& DT_VCENTER
)
1154 if (flags
& DT_CALCRECT
)
1156 if (rect
->bottom
- rect
->top
< size
.cy
/ 2)
1157 y
= rect
->top
+ (invert_y
? size
.cy
: -size
.cy
) / 2;
1161 y
= rect
->top
+ (rect
->bottom
- rect
->top
+ (invert_y
? size
.cy
: -size
.cy
)) / 2;
1164 else if (flags
& DT_BOTTOM
)
1165 y
= rect
->bottom
+ (invert_y
? 0 : -size
.cy
);
1167 if (flags
& DT_VCENTER
) y
= rect
->top
+
1168 (rect
->bottom
- rect
->top
) / 2 - size
.cy
/ 2;
1169 else if (flags
& DT_BOTTOM
) y
= rect
->bottom
- size
.cy
;
1173 if (!(flags
& DT_CALCRECT
))
1175 const WCHAR
*str
= line
;
1181 if ((flags
& DT_EXPANDTABS
))
1184 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
1186 if (len_seg
!= len
&&
1188 !GreGetTextExtentW(hdc
, str
, len_seg
, &size
, 0))
1190 !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
1194 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1196 HeapFree (GetProcessHeap(), 0, retstr
);
1204 if (!GreExtTextOutW( hdc
, xseg
, y
,
1205 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1206 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1207 rect
, str
, len_seg
, NULL
, 0 ))
1209 if (!ExtTextOutW( hdc
, xseg
, y
,
1210 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1211 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1212 rect
, str
, len_seg
, NULL
))
1216 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1218 HeapFree (GetProcessHeap(), 0, retstr
);
1222 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
1224 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
, (flags
& DT_NOCLIP
) ? NULL
: rect
);
1230 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
1232 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
1233 if (prefix_offset
!= -1)
1235 if (prefix_offset
< len_seg
)
1237 /* We have just drawn an underscore; we ought to
1238 * figure out where the next one is. I am going
1239 * to leave it for now until I have a better model
1240 * for the line, which will make reprefixing easier.
1241 * This is where ellip would be used.
1246 prefix_offset
-= len_seg
;
1251 else if (size
.cx
> max_width
)
1252 max_width
= size
.cx
;
1254 y
+= invert_y
? -lh
: lh
;
1256 dtp
->uiLengthDrawn
+= len
;
1258 while (strPtr
&& !last_line
);
1261 if (!(flags
& DT_NOCLIP
) )
1263 SelectClipRgn(hdc
, hrgn
);
1270 if (!(flags
& DT_NOCLIP
) )
1272 NtGdiExtSelectClipRgn(hdc
, hrgn
, RGN_COPY
);
1275 GreDeleteObject(hrgn
);
1280 if (flags
& DT_CALCRECT
)
1282 rect
->right
= rect
->left
+ max_width
;
1285 rect
->right
+= lmargin
+ rmargin
;
1289 memcpy (str
, retstr
, size_retstr
);
1291 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1293 HeapFree (GetProcessHeap(), 0, retstr
);
1296 return y
- rect
->top
;