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 /***********************************************************************
69 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
70 * Note: this doesn't work too well for text-alignment modes other
71 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
73 /* WINE synced 22-May-2006 */
74 LONG
TEXT_TabbedTextOut( HDC hdc
,
101 GreGetTextMetricsW( hdc
, &tm
);
104 GetTextMetricsA( hdc
, &tm
);
106 defWidth
= 8 * tm
.tmAveCharWidth
;
115 /* chop the string into substrings of 0 or more <tabs>
116 * possibly followed by 1 or more normal characters */
117 for (i
= 0; i
< count
; i
++)
118 if (lpstr
[i
] != '\t') break;
119 for (j
= i
; j
< count
; j
++)
120 if (lpstr
[j
] == '\t') break;
121 /* get the extent of the normal character part */
123 GreGetTextExtentW( hdc
, (LPWSTR
)lpstr
+ i
, j
- i
, &extent
, 0 );
125 GetTextExtentPointW( hdc
, lpstr
+ i
, j
- i
, &extent
);
127 /* and if there is a <tab>, calculate its position */
129 /* get x coordinate for the drawing of this string */
130 for (; cTabStops
> i
; lpTabPos
++, cTabStops
--)
132 if( nTabOrg
+ abs( *lpTabPos
) > x
) {
133 if( lpTabPos
[ i
- 1] >= 0) {
134 /* a left aligned tab */
135 x
= nTabOrg
+ lpTabPos
[ i
-1] + extent
.cx
;
140 /* if tab pos is negative then text is right-aligned
141 * to tab stop meaning that the string extends to the
142 * left, so we must subtract the width of the string */
143 if (nTabOrg
- lpTabPos
[ i
- 1] - extent
.cx
> x
)
145 x
= nTabOrg
- lpTabPos
[ i
- 1];
152 /* if we have run out of tab stops and we have a valid default tab
153 * stop width then round x up to that width */
154 if ((cTabStops
<= i
) && (defWidth
> 0)) {
155 x0
= nTabOrg
+ ((x
- nTabOrg
) / defWidth
+ i
) * defWidth
;
157 } else if ((cTabStops
<= i
) && (defWidth
< 0)) {
158 x
= nTabOrg
+ ((x
- nTabOrg
+ extent
.cx
) / -defWidth
+ i
)
169 r
.bottom
= y
+ extent
.cy
;
171 GreExtTextOutW( hdc
, x0
, y
, GreGetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
172 &r
, (LPWSTR
)lpstr
+ i
, j
- i
, NULL
, 0 );
174 ExtTextOutW( hdc
, x0
, y
, GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
175 &r
, lpstr
+ i
, j
- i
, NULL
);
181 return MAKELONG(x
- start
, extent
.cy
);
184 /*********************************************************************
189 * Copyright 1993, 1994 Alexandre Julliard
190 * Copyright 2002 Bill Medland
193 * How many buffers to use
194 * While processing in DrawText there are potentially three different forms
195 * of the text that need to be held. How are they best held?
196 * 1. The original text is needed, of course, to see what to display.
197 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
199 * 3. The buffered text that is about to be displayed e.g. the current line.
200 * Typically this will exclude the ampersands used for prefixing etc.
203 * a. If the buffered text to be displayed includes the ampersands then
204 * we will need special measurement and draw functions that will ignore
205 * the ampersands (e.g. by copying to a buffer without the prefix and
206 * then using the normal forms). This may involve less space but may
207 * require more processing. e.g. since a line containing tabs may
208 * contain several underlined characters either we need to carry around
209 * a list of prefix locations or we may need to locate them several
211 * b. If we actually directly modify the "original text" as we go then we
212 * will need some special "caching" to handle the fact that when we
213 * ellipsify the text the ellipsis may modify the next line of text,
214 * which we have not yet processed. (e.g. ellipsification of a W at the
215 * end of a line will overwrite the W, the \n and the first character of
216 * the next line, and a \0 will overwrite the second. Try it!!)
218 * Option 1. Three separate storages. (To be implemented)
219 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
220 * the edited string in some form, either as the string itself or as some
221 * sort of "edit list" to be applied just before returning.
222 * Use a buffer that holds the ellipsified current line sans ampersands
223 * and accept the need occasionally to recalculate the prefixes (if
224 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
232 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
233 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
235 #define FORWARD_SLASH '/'
236 #define BACK_SLASH '\\'
238 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
240 typedef struct tag_ellipsis_data
248 /*********************************************************************
249 * TEXT_Ellipsify (static)
251 * Add an ellipsis to the end of the given string whilst ensuring it fits.
253 * If the ellipsis alone doesn't fit then it will be returned anyway.
255 * See Also TEXT_PathEllipsify
258 * hdc [in] The handle to the DC that defines the font.
259 * str [in/out] The string that needs to be modified.
260 * max_str [in] The dimension of str (number of WCHAR).
261 * len_str [in/out] The number of characters in str
262 * width [in] The maximum width permitted (in logical coordinates)
263 * size [out] The dimensions of the text
264 * modstr [out] The modified form of the string, to be returned to the
265 * calling program. It is assumed that the caller has
266 * made sufficient space available so we don't need to
267 * know the size of the space. This pointer may be NULL if
268 * the modified string is not required.
269 * len_before [out] The number of characters before the ellipsis.
270 * len_ellip [out] The number of characters in the ellipsis.
272 * See for example Microsoft article Q249678.
274 * For now we will simply use three dots rather than worrying about whether
275 * the font contains an explicit ellipsis character.
277 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
278 unsigned int *len_str
, int width
, SIZE
*size
,
280 int *len_before
, int *len_ellip
)
282 unsigned int len_ellipsis
;
283 unsigned int lo
, mid
, hi
;
285 len_ellipsis
= wcslen (ELLIPSISW
);
287 len_ellipsis
= strlenW (ELLIPSISW
);
289 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
290 if (*len_str
> max_len
- len_ellipsis
)
291 *len_str
= max_len
- len_ellipsis
;
293 /* First do a quick binary search to get an upper bound for *len_str. */
296 GreGetTextExtentExW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
, 0) &&
298 GetTextExtentExPointW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
) &&
302 for (lo
= 0, hi
= *len_str
; lo
< hi
; )
306 if (!GreGetTextExtentExW(hdc
, str
, mid
, width
, NULL
, NULL
, size
, 0))
308 if (!GetTextExtentExPointW(hdc
, str
, mid
, width
, NULL
, NULL
, size
))
311 if (size
->cx
> width
)
318 /* Now this should take only a couple iterations at most. */
321 memcpy(str
+ *len_str
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
323 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
324 NULL
, NULL
, size
, 0)) break;
326 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
327 NULL
, NULL
, size
)) break;
329 if (!*len_str
|| size
->cx
<= width
) break;
333 *len_ellip
= len_ellipsis
;
334 *len_before
= *len_str
;
335 *len_str
+= len_ellipsis
;
339 memcpy (modstr
, str
, *len_str
* sizeof(WCHAR
));
340 modstr
[*len_str
] = '\0';
344 /*********************************************************************
345 * TEXT_PathEllipsify (static)
347 * Add an ellipsis to the provided string in order to make it fit within
348 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
351 * See Also TEXT_Ellipsify
354 * hdc [in] The handle to the DC that defines the font.
355 * str [in/out] The string that needs to be modified
356 * max_str [in] The dimension of str (number of WCHAR).
357 * len_str [in/out] The number of characters in str
358 * width [in] The maximum width permitted (in logical coordinates)
359 * size [out] The dimensions of the text
360 * modstr [out] The modified form of the string, to be returned to the
361 * calling program. It is assumed that the caller has
362 * made sufficient space available so we don't need to
363 * know the size of the space. This pointer may be NULL if
364 * the modified string is not required.
365 * pellip [out] The ellipsification results
367 * For now we will simply use three dots rather than worrying about whether
368 * the font contains an explicit ellipsis character.
370 * The following applies, I think to Win95. We will need to extend it for
371 * Win98 which can have both path and end ellipsis at the same time (e.g.
372 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
374 * The resulting string consists of as much as possible of the following:
375 * 1. The ellipsis itself
376 * 2. The last \ or / of the string (if any)
377 * 3. Everything after the last \ or / of the string (if any) or the whole
378 * string if there is no / or \. I believe that under Win95 this would
379 * include everything even though some might be clipped off the end whereas
380 * under Win98 that might be ellipsified too.
381 * Yet to be investigated is whether this would include wordbreaking if the
382 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
383 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
384 * broken within words).
385 * 4. All the stuff before the / or \, which is placed before the ellipsis.
387 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
388 unsigned int *len_str
, int width
, SIZE
*size
,
389 WCHAR
*modstr
, ellipsis_data
*pellip
)
394 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
396 len_ellipsis
= wcslen (ELLIPSISW
);
398 len_ellipsis
= strlenW (ELLIPSISW
);
400 if (!max_len
) return;
401 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
402 if (*len_str
+ len_ellipsis
>= max_len
)
403 *len_str
= max_len
- len_ellipsis
-1;
404 /* Hopefully this will never happen, otherwise it would probably lose
405 * the wrong character
407 str
[*len_str
] = '\0'; /* to simplify things */
409 lastBkSlash
= wcsrchr (str
, BACK_SLASH
);
410 lastFwdSlash
= wcsrchr (str
, FORWARD_SLASH
);
412 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
413 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
415 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
416 if (!lastSlash
) lastSlash
= str
;
417 len_trailing
= *len_str
- (lastSlash
- str
);
419 /* overlap-safe movement to the right */
420 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
421 memcpy (lastSlash
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
422 len_trailing
+= len_ellipsis
;
423 /* From this point on lastSlash actually points to the ellipsis in front
424 * of the last slash and len_trailing includes the ellipsis
431 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
432 NULL
, NULL
, size
, 0)) break;
434 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
435 NULL
, NULL
, size
)) break;
437 if (lastSlash
== str
|| size
->cx
<= width
) break;
439 /* overlap-safe movement to the left */
440 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
447 pellip
->before
= lastSlash
-str
;
448 pellip
->len
= len_ellipsis
;
449 pellip
->under
= len_under
;
450 pellip
->after
= len_trailing
- len_ellipsis
;
451 *len_str
+= len_ellipsis
;
455 memcpy(modstr
, str
, *len_str
* sizeof(WCHAR
));
456 modstr
[*len_str
] = '\0';
460 /* Check the character is Chinese, Japanese, Korean and/or Thai */
461 inline BOOL
IsCJKT(WCHAR wch
)
463 if (0x0E00 <= wch
&& wch
<= 0x0E7F)
464 return TRUE
; /* Thai */
466 if (0x3000 <= wch
&& wch
<= 0x9FFF)
467 return TRUE
; /* CJK */
469 if (0xAC00 <= wch
&& wch
<= 0xD7FF)
470 return TRUE
; /* Korean */
472 if (0xFF00 <= wch
&& wch
<= 0xFFEF)
473 return TRUE
; /* CJK */
478 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
479 static const WCHAR KinsokuClassA
[] =
481 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
482 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
483 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
484 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
485 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
486 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
487 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
488 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
489 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
492 /*********************************************************************
493 * TEXT_WordBreak (static)
495 * Perform wordbreak processing on the given string
497 * Assumes that DT_WORDBREAK has been specified and not all the characters
498 * fit. Note that this function should even be called when the first character
499 * that doesn't fit is known to be a space or tab, so that it can swallow them.
501 * Note that the Windows processing has some strange properties.
502 * 1. If the text is left-justified and there is room for some of the spaces
503 * that follow the last word on the line then those that fit are included on
505 * 2. If the text is centred or right-justified and there is room for some of
506 * the spaces that follow the last word on the line then all but one of those
507 * that fit are included on the line.
508 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
509 * character of a new line it will be skipped.
512 * hdc [in] The handle to the DC that defines the font.
513 * str [in/out] The string that needs to be broken.
514 * max_str [in] The dimension of str (number of WCHAR).
515 * len_str [in/out] The number of characters in str
516 * width [in] The maximum width permitted
517 * format [in] The format flags in effect
518 * chars_fit [in] The maximum number of characters of str that are already
519 * known to fit; chars_fit+1 is known not to fit.
520 * chars_used [out] The number of characters of str that have been "used" and
521 * do not need to be included in later text. For example this will
522 * include any spaces that have been discarded from the start of
524 * size [out] The size of the returned text in logical coordinates
526 * Pedantic assumption - Assumes that the text length is monotonically
527 * increasing with number of characters (i.e. no weird kernings)
531 * Work back from the last character that did fit to either a space or the last
532 * character of a word, whichever is met first.
533 * If there was one or the first character didn't fit then
534 * If the text is centred or right justified and that one character was a
535 * space then break the line before that character
536 * Otherwise break the line after that character
537 * and if the next character is a space then discard it.
538 * Suppose there was none (and the first character did fit).
539 * If Break Within Word is permitted
540 * break the word after the last character that fits (there must be
541 * at least one; none is caught earlier).
543 * discard any trailing space.
544 * include the whole word; it may be ellipsified later
546 * Break Within Word is permitted under a set of circumstances that are not
547 * totally clear yet. Currently our best guess is:
548 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
549 * DT_PATH_ELLIPSIS is
552 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
553 unsigned int *len_str
,
554 int width
, int format
, unsigned int chars_fit
,
555 unsigned int *chars_used
, SIZE
*size
)
559 assert (format
& DT_WORDBREAK
);
560 assert (chars_fit
< *len_str
);
562 /* Work back from the last character that did fit to either a space or the
563 * last character of a word, whichever is met first.
565 p
= str
+ chars_fit
; /* The character that doesn't fit */
569 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
570 p
--; /* the word just fitted */
573 while (p
> str
&& *(--p
) != SPACE
&& (!IsCJKT(p
[1]) ||
574 p
[1] == L
'\0' || wcschr(KinsokuClassA
, p
[1]) != NULL
))
576 word_fits
= (p
!= str
|| *p
== SPACE
|| IsCJKT(p
[1]));
578 /* If there was one. */
582 /* break the line before/after that character */
583 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
585 next_is_space
= (unsigned int) (p
- str
) < *len_str
&& *p
== SPACE
;
587 /* and if the next character is a space then discard it. */
588 *chars_used
= *len_str
;
592 /* Suppose there was none. */
595 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
598 /* break the word after the last character that fits (there must be
602 *len_str
= chars_fit
;
603 *chars_used
= chars_fit
;
605 /* FIXME - possible error. Since the next character is now removed
606 * this could make the text longer so that it no longer fits, and
607 * so we need a loop to test and shrink.
613 /* discard any trailing space. */
614 const WCHAR
*e
= str
+ *len_str
;
616 while (p
< e
&& *p
!= SPACE
)
618 *chars_used
= p
- str
;
619 if (p
< e
) /* i.e. loop failed because *p == SPACE */
622 /* include the whole word; it may be ellipsified later */
624 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
625 * so that it will be too long
629 /* Remeasure the string */
631 GreGetTextExtentExW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
, 0);
633 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
637 /*********************************************************************
640 * Skip over the given number of characters, bearing in mind prefix
641 * substitution and the fact that a character may take more than one
642 * WCHAR (Unicode surrogates are two words long) (and there may have been
646 * new_count [out] The updated count
647 * new_str [out] The updated pointer
648 * start_count [in] The count of remaining characters corresponding to the
649 * start of the string
650 * start_str [in] The starting point of the string
651 * max [in] The number of characters actually in this segment of the
652 * string (the & counts)
653 * n [in] The number of characters to skip (if prefix then
655 * prefix [in] Apply prefix substitution
661 * There must be at least n characters in the string
662 * We need max because the "line" may have ended with a & followed by a tab
663 * or newline etc. which we don't want to swallow
666 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
667 int start_count
, const WCHAR
*start_str
,
668 int max
, int n
, int prefix
)
670 /* This is specific to wide characters, MSDN doesn't say anything much
671 * about Unicode surrogates yet and it isn't clear if _wcsinc will
672 * correctly handle them so we'll just do this the easy way for now
677 const WCHAR
*str_on_entry
= start_str
;
682 if ((*start_str
== PREFIX
|| *start_str
== ALPHA_PREFIX
) && max
--)
686 start_count
-= (start_str
- str_on_entry
);
693 *new_str
= start_str
;
694 *new_count
= start_count
;
697 /*********************************************************************
700 * Reanalyse the text to find the prefixed character. This is called when
701 * wordbreaking or ellipsification has shortened the string such that the
702 * previously noted prefixed character is no longer visible.
705 * str [in] The original string segment (including all characters)
706 * ns [in] The number of characters in str (including prefixes)
707 * pe [in] The ellipsification data
710 * The prefix offset within the new string segment (the one that contains the
711 * ellipses and does not contain the prefix characters) (-1 if none)
714 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int ns
,
715 const ellipsis_data
*pe
)
719 unsigned int n
= pe
->before
+ pe
->under
+ pe
->after
;
721 for (i
= 0; i
< n
; i
++, str
++)
723 if (i
== (unsigned int) pe
->before
)
725 /* Reached the path ellipsis; jump over it */
726 if (ns
< (unsigned int) pe
->under
) break;
730 if (!pe
->after
) break; /* Nothing after the path ellipsis */
734 if (*str
++ == PREFIX
|| *str
== ALPHA_PREFIX
)
739 result
= (i
< (unsigned int) pe
->before
|| pe
->under
== 0) ? i
: i
- pe
->under
+ pe
->len
;
740 /* pe->len may be non-zero while pe_under is zero */
747 /*********************************************************************
748 * Returns true if and only if the remainder of the line is a single
749 * newline representation or nothing
752 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
754 if (!num_chars
) return TRUE
;
755 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
756 if (!--num_chars
) return TRUE
;
757 if (*str
== *(str
+1)) return FALSE
;
759 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
760 if (--num_chars
) return FALSE
;
764 /*********************************************************************
765 * Return next line of text from a string.
767 * hdc - handle to DC.
768 * str - string to parse into lines.
769 * count - length of str.
770 * dest - destination in which to return line.
771 * len - dest buffer size in chars on input, copied length into dest on output.
772 * width - maximum width of line in pixels.
773 * format - format type passed to DrawText.
774 * retsize - returned size of the line in pixels.
775 * last_line - TRUE if is the last line that will be processed
776 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
777 * the return string is built.
778 * tabwidth - The width of a tab in logical coordinates
779 * pprefix_offset - Here is where we return the offset within dest of the first
780 * prefixed (underlined) character. -1 is returned if there
781 * are none. Note that there may be more; the calling code
782 * will need to use TEXT_Reprefix to find any later ones.
783 * pellip - Here is where we return the information about any ellipsification
784 * that was carried out. Note that if tabs are being expanded then
785 * this data will correspond to the last text segment actually
786 * returned in dest; by definition there would not have been any
787 * ellipsification in earlier text segments of the line.
789 * Returns pointer to next char in str after end of the line
790 * or NULL if end of str reached.
792 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
793 WCHAR
*dest
, int *len
, int width
, DWORD format
,
794 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
,
795 int tabwidth
, int *pprefix_offset
,
796 ellipsis_data
*pellip
)
802 int seg_i
, seg_count
, seg_j
;
807 unsigned int j_in_seg
;
809 *pprefix_offset
= -1;
811 /* For each text segment in the line */
817 /* Skip any leading tabs */
819 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
821 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
822 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
823 while (*count
&& str
[i
] == TAB
)
826 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
831 /* Now copy as far as the next tab or cr/lf or eos */
838 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
839 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
841 if ((format
& DT_NOPREFIX
) || *count
<= 1)
843 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
847 if (str
[i
] == PREFIX
|| str
[i
] == ALPHA_PREFIX
) {
848 (*count
)--, i
++; /* Throw away the prefix itself */
849 if (str
[i
] == PREFIX
)
851 /* Swallow it before we see it again */
852 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
854 else if (*pprefix_offset
== -1 || *pprefix_offset
>= seg_j
)
858 /* else the previous prefix was in an earlier segment of the
859 * line; we will leave it to the drawing code to catch this
863 else if (str
[i
] == KANA_PREFIX
)
865 /* Throw away katakana access keys */
866 (*count
)--, i
++; /* skip the prefix */
867 (*count
)--, i
++; /* skip the letter */
871 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
876 /* Measure the whole text segment and possibly WordBreak and
880 j_in_seg
= j
- seg_j
;
881 max_seg_width
= width
- plen
;
883 GreGetTextExtentExW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, (PULONG
)&num_fit
, NULL
, &size
, 0);
885 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
888 /* The Microsoft handling of various combinations of formats is weird.
889 * The following may very easily be incorrect if several formats are
890 * combined, and may differ between versions (to say nothing of the
891 * several bugs in the Microsoft versions).
894 line_fits
= (num_fit
>= j_in_seg
);
895 if (!line_fits
&& (format
& DT_WORDBREAK
))
898 unsigned int chars_used
;
899 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
900 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
901 line_fits
= (size
.cx
<= max_seg_width
);
902 /* and correct the counts */
903 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
904 chars_used
, !(format
& DT_NOPREFIX
));
908 pellip
->before
= j_in_seg
;
913 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
915 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
916 max_seg_width
, &size
, *p_retstr
, pellip
);
917 line_fits
= (size
.cx
<= max_seg_width
);
920 /* NB we may end up ellipsifying a word-broken or path_ellipsified
922 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
923 ((format
& DT_END_ELLIPSIS
) &&
924 ((last_line
&& *count
) ||
925 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
927 int before
, len_ellipsis
;
928 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
929 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
930 if (before
> pellip
->before
)
932 /* We must have done a path ellipsis too */
933 pellip
->after
= before
- pellip
->before
- pellip
->len
;
934 /* Leave the len as the length of the first ellipsis */
938 /* If we are here after a path ellipsification it must be
939 * because even the ellipsis itself didn't fit.
941 assert (pellip
->under
== 0 && pellip
->after
== 0);
942 pellip
->before
= before
;
943 pellip
->len
= len_ellipsis
;
944 /* pellip->after remains as zero as does
948 line_fits
= (size
.cx
<= max_seg_width
);
951 /* As an optimisation if we have ellipsified and we are expanding
952 * tabs and we haven't reached the end of the line we can skip to it
953 * now rather than going around the loop again.
955 if ((format
& DT_EXPANDTABS
) && ellipsified
)
957 if (format
& DT_SINGLELINE
)
961 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
968 j
= seg_j
+ j_in_seg
;
969 if (*pprefix_offset
>= seg_j
+ pellip
->before
)
971 *pprefix_offset
= TEXT_Reprefix (str
+ seg_i
, i
- seg_i
, pellip
);
972 if (*pprefix_offset
!= -1)
973 *pprefix_offset
+= seg_j
;
977 if (size
.cy
> retsize
->cy
)
978 retsize
->cy
= size
.cy
;
984 else if (str
[i
] == CR
|| str
[i
] == LF
)
987 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
993 /* else it was a Tab and we go around again */
1005 /***********************************************************************
1006 * TEXT_DrawUnderscore
1008 * Draw the underline under the prefixed character
1011 * hdc [in] The handle of the DC for drawing
1012 * x [in] The x location of the line segment (logical coordinates)
1013 * y [in] The y location of where the underscore should appear
1014 * (logical coordinates)
1015 * str [in] The text of the line segment
1016 * offset [in] The offset of the underscored character within str
1017 * rect [in] Clipping rectangle (if not NULL)
1019 /* Synced with wine 1.1.32 */
1020 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
, const RECT
*rect
)
1028 GreGetTextExtentW (hdc
, (LPWSTR
)str
, offset
, &size
, 0);
1030 GetTextExtentPointW (hdc
, str
, offset
, &size
);
1032 prefix_x
= x
+ size
.cx
;
1034 GreGetTextExtentW (hdc
, (LPWSTR
)str
, offset
+1, &size
, 0);
1036 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
1038 prefix_end
= x
+ size
.cx
- 1;
1039 /* The above method may eventually be slightly wrong due to kerning etc. */
1041 /* Check for clipping */
1044 if (prefix_x
> rect
->right
|| prefix_end
< rect
->left
||
1045 y
< rect
->top
|| y
> rect
->bottom
)
1046 return; /* Completely outside */
1047 /* Partially outside */
1048 if (prefix_x
< rect
->left
) prefix_x
= rect
->left
;
1049 if (prefix_end
> rect
->right
) prefix_end
= rect
->right
;
1052 hpen
= NtGdiCreatePen (PS_SOLID
, 1, GreGetTextColor (hdc
), NULL
);
1053 oldPen
= NtGdiSelectPen (hdc
, hpen
);
1054 GreMoveTo (hdc
, prefix_x
, y
, NULL
);
1055 NtGdiLineTo (hdc
, prefix_end
, y
);
1056 NtGdiSelectPen (hdc
, oldPen
);
1057 GreDeleteObject (hpen
);
1059 hpen
= CreatePen (PS_SOLID
, 1, GetTextColor (hdc
));
1060 oldPen
= SelectObject (hdc
, hpen
);
1061 MoveToEx (hdc
, prefix_x
, y
, NULL
);
1062 LineTo (hdc
, prefix_end
, y
);
1063 SelectObject (hdc
, oldPen
);
1064 DeleteObject (hpen
);
1068 /***********************************************************************
1069 * DrawTextExW (USER32.@)
1071 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1072 * is not quite complete, especially with regard to \0. We will assume that
1073 * the returned string could have a length of up to i_count+3 and also have
1074 * a trailing \0 (which would be 4 more than a not-null-terminated string but
1075 * 3 more than a null-terminated string). If this is not so then increase
1076 * the allowance in DrawTextExA.
1078 #define MAX_BUFFER 1024
1082 * Synced with Wine Staging 1.7.37
1084 INT WINAPI
DrawTextExWorker( HDC hdc
,
1089 LPDRAWTEXTPARAMS dtp
)
1092 const WCHAR
*strPtr
;
1093 WCHAR
*retstr
, *p_retstr
;
1095 WCHAR line
[MAX_BUFFER
];
1096 int len
, lh
, count
=i_count
;
1098 int lmargin
= 0, rmargin
= 0;
1099 int x
= rect
->left
, y
= rect
->top
;
1100 int width
= rect
->right
- rect
->left
;
1103 int tabwidth
/* to keep gcc happy */ = 0;
1105 ellipsis_data ellip
;
1106 BOOL invert_y
=FALSE
;
1108 TRACE("%S, %d, %08x\n", str
, count
, flags
);
1110 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str
, count
), count
,
1111 wine_dbgstr_rect(rect
), flags
);
1113 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1114 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
1120 if (flags
& DT_SINGLELINE
)
1121 flags
&= ~DT_WORDBREAK
;
1123 GreGetTextMetricsW(hdc
, &tm
);
1125 GetTextMetricsW(hdc
, &tm
);
1127 if (flags
& DT_EXTERNALLEADING
)
1128 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1132 if (str
[0] && count
== 0)
1135 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1138 if (GreGetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1140 SIZE window_ext
, viewport_ext
;
1141 GreGetWindowExtEx(hdc
, &window_ext
);
1142 GreGetViewportExtEx(hdc
, &viewport_ext
);
1143 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1147 if (GetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1149 SIZE window_ext
, viewport_ext
;
1150 GetWindowExtEx(hdc
, &window_ext
);
1151 GetViewportExtEx(hdc
, &viewport_ext
);
1152 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1159 count
= wcslen(str
);
1161 count
= strlenW(str
);
1165 if( flags
& DT_CALCRECT
)
1167 rect
->right
= rect
->left
;
1168 if( flags
& DT_SINGLELINE
)
1169 rect
->bottom
= rect
->top
+ (invert_y
? -lh
: lh
);
1171 rect
->bottom
= rect
->top
;
1179 lmargin
= dtp
->iLeftMargin
;
1180 rmargin
= dtp
->iRightMargin
;
1181 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
1183 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
1186 if (flags
& DT_EXPANDTABS
)
1188 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
) ? dtp
->iTabLength
: 8;
1189 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
1192 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
1194 if (flags
& DT_MODIFYSTRING
)
1196 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
1198 retstr
= ExAllocatePoolWithTag(PagedPool
, size_retstr
, USERTAG_RTL
);
1200 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
1202 if (!retstr
) return 0;
1203 memcpy (retstr
, str
, size_retstr
);
1214 len
= sizeof(line
)/sizeof(line
[0]);
1216 last_line
= !(flags
& DT_NOCLIP
) && y
- ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) < rect
->bottom
;
1218 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
1219 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
, tabwidth
, &prefix_offset
, &ellip
);
1222 if (flags
& DT_CENTER
)
1224 if (((rect
->right
- rect
->left
) < size
.cx
) && (flags
& DT_CALCRECT
))
1226 x
= rect
->left
+ size
.cx
;
1230 x
= (rect
->left
+ rect
->right
- size
.cx
) / 2;
1234 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
1237 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
1239 if (flags
& DT_SINGLELINE
)
1241 if (flags
& DT_VCENTER
)
1244 if (((rect
->bottom
- rect
->top
) < (invert_y
? -size
.cy
: size
.cy
)) && (flags
& DT_CALCRECT
))
1246 y
= rect
->top
+ (invert_y
? -size
.cy
: size
.cy
);
1251 y
= rect
->top
+ (rect
->bottom
- rect
->top
) / 2 + (invert_y
? (size
.cy
/ 2) : (-size
.cy
/ 2));
1256 else if (flags
& DT_BOTTOM
)
1257 y
= rect
->bottom
+ (invert_y
? 0 : -size
.cy
);
1260 if (!(flags
& DT_CALCRECT
))
1262 const WCHAR
*str
= line
;
1268 if ((flags
& DT_EXPANDTABS
))
1271 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
1273 if (len_seg
!= len
&&
1275 !GreGetTextExtentW(hdc
, (LPWSTR
)str
, len_seg
, &size
, 0))
1277 !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
1281 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1283 HeapFree (GetProcessHeap(), 0, retstr
);
1291 if (!GreExtTextOutW( hdc
, xseg
, y
,
1292 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1293 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1294 rect
, (LPWSTR
)str
, len_seg
, NULL
, 0 ))
1296 if (!ExtTextOutW( hdc
, xseg
, y
,
1297 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1298 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1299 rect
, str
, len_seg
, NULL
))
1303 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1305 HeapFree (GetProcessHeap(), 0, retstr
);
1309 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
1311 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
, (flags
& DT_NOCLIP
) ? NULL
: rect
);
1317 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
1319 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
1320 if (prefix_offset
!= -1)
1322 if (prefix_offset
< len_seg
)
1324 /* We have just drawn an underscore; we ought to
1325 * figure out where the next one is. I am going
1326 * to leave it for now until I have a better model
1327 * for the line, which will make reprefixing easier.
1328 * This is where ellip would be used.
1333 prefix_offset
-= len_seg
;
1338 else if (size
.cx
> max_width
)
1339 max_width
= size
.cx
;
1341 y
+= invert_y
? -lh
: lh
;
1343 dtp
->uiLengthDrawn
+= len
;
1345 while (strPtr
&& !last_line
);
1347 if (flags
& DT_CALCRECT
)
1349 rect
->right
= rect
->left
+ max_width
;
1352 rect
->right
+= lmargin
+ rmargin
;
1356 memcpy (str
, retstr
, size_retstr
);
1358 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1360 HeapFree (GetProcessHeap(), 0, retstr
);
1363 return y
- rect
->top
;