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
,
94 GreGetTextMetricsW( hdc
, &tm
);
96 GetTextMetricsW( hdc
, &tm
);
101 defWidth
= *lpTabPos
;
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
, 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
, lpstr
+ i
, j
- i
, NULL
, 0 );
174 ExtTextOutW( hdc
, x0
, y
, GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
175 &r
, lpstr
+ i
, j
- i
, NULL
);
183 extent
.cy
= tm
.tmHeight
;
185 return MAKELONG(x
- start
, extent
.cy
);
188 /*********************************************************************
193 * Copyright 1993, 1994 Alexandre Julliard
194 * Copyright 2002 Bill Medland
197 * How many buffers to use
198 * While processing in DrawText there are potentially three different forms
199 * of the text that need to be held. How are they best held?
200 * 1. The original text is needed, of course, to see what to display.
201 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
203 * 3. The buffered text that is about to be displayed e.g. the current line.
204 * Typically this will exclude the ampersands used for prefixing etc.
207 * a. If the buffered text to be displayed includes the ampersands then
208 * we will need special measurement and draw functions that will ignore
209 * the ampersands (e.g. by copying to a buffer without the prefix and
210 * then using the normal forms). This may involve less space but may
211 * require more processing. e.g. since a line containing tabs may
212 * contain several underlined characters either we need to carry around
213 * a list of prefix locations or we may need to locate them several
215 * b. If we actually directly modify the "original text" as we go then we
216 * will need some special "caching" to handle the fact that when we
217 * ellipsify the text the ellipsis may modify the next line of text,
218 * which we have not yet processed. (e.g. ellipsification of a W at the
219 * end of a line will overwrite the W, the \n and the first character of
220 * the next line, and a \0 will overwrite the second. Try it!!)
222 * Option 1. Three separate storages. (To be implemented)
223 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
224 * the edited string in some form, either as the string itself or as some
225 * sort of "edit list" to be applied just before returning.
226 * Use a buffer that holds the ellipsified current line sans ampersands
227 * and accept the need occasionally to recalculate the prefixes (if
228 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
236 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
237 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
239 #define FORWARD_SLASH '/'
240 #define BACK_SLASH '\\'
242 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
244 typedef struct tag_ellipsis_data
252 /*********************************************************************
253 * TEXT_Ellipsify (static)
255 * Add an ellipsis to the end of the given string whilst ensuring it fits.
257 * If the ellipsis alone doesn't fit then it will be returned anyway.
259 * See Also TEXT_PathEllipsify
262 * hdc [in] The handle to the DC that defines the font.
263 * str [in/out] The string that needs to be modified.
264 * max_str [in] The dimension of str (number of WCHAR).
265 * len_str [in/out] The number of characters in str
266 * width [in] The maximum width permitted (in logical coordinates)
267 * size [out] The dimensions of the text
268 * modstr [out] The modified form of the string, to be returned to the
269 * calling program. It is assumed that the caller has
270 * made sufficient space available so we don't need to
271 * know the size of the space. This pointer may be NULL if
272 * the modified string is not required.
273 * len_before [out] The number of characters before the ellipsis.
274 * len_ellip [out] The number of characters in the ellipsis.
276 * See for example Microsoft article Q249678.
278 * For now we will simply use three dots rather than worrying about whether
279 * the font contains an explicit ellipsis character.
281 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
282 unsigned int *len_str
, int width
, SIZE
*size
,
284 int *len_before
, int *len_ellip
)
286 unsigned int len_ellipsis
;
287 unsigned int lo
, mid
, hi
;
289 len_ellipsis
= wcslen (ELLIPSISW
);
291 len_ellipsis
= strlenW (ELLIPSISW
);
293 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
294 if (*len_str
> max_len
- len_ellipsis
)
295 *len_str
= max_len
- len_ellipsis
;
297 /* First do a quick binary search to get an upper bound for *len_str. */
300 GreGetTextExtentExW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
, 0) &&
302 GetTextExtentExPointW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
) &&
306 for (lo
= 0, hi
= *len_str
; lo
< hi
; )
310 if (!GreGetTextExtentExW(hdc
, str
, mid
, width
, NULL
, NULL
, size
, 0))
312 if (!GetTextExtentExPointW(hdc
, str
, mid
, width
, NULL
, NULL
, size
))
315 if (size
->cx
> width
)
322 /* Now this should take only a couple iterations at most. */
325 memcpy(str
+ *len_str
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
327 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
328 NULL
, NULL
, size
, 0)) break;
330 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
331 NULL
, NULL
, size
)) break;
333 if (!*len_str
|| size
->cx
<= width
) break;
337 *len_ellip
= len_ellipsis
;
338 *len_before
= *len_str
;
339 *len_str
+= len_ellipsis
;
343 memcpy (modstr
, str
, *len_str
* sizeof(WCHAR
));
344 modstr
[*len_str
] = '\0';
348 /*********************************************************************
349 * TEXT_PathEllipsify (static)
351 * Add an ellipsis to the provided string in order to make it fit within
352 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
355 * See Also TEXT_Ellipsify
358 * hdc [in] The handle to the DC that defines the font.
359 * str [in/out] The string that needs to be modified
360 * max_str [in] The dimension of str (number of WCHAR).
361 * len_str [in/out] The number of characters in str
362 * width [in] The maximum width permitted (in logical coordinates)
363 * size [out] The dimensions of the text
364 * modstr [out] The modified form of the string, to be returned to the
365 * calling program. It is assumed that the caller has
366 * made sufficient space available so we don't need to
367 * know the size of the space. This pointer may be NULL if
368 * the modified string is not required.
369 * pellip [out] The ellipsification results
371 * For now we will simply use three dots rather than worrying about whether
372 * the font contains an explicit ellipsis character.
374 * The following applies, I think to Win95. We will need to extend it for
375 * Win98 which can have both path and end ellipsis at the same time (e.g.
376 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
378 * The resulting string consists of as much as possible of the following:
379 * 1. The ellipsis itself
380 * 2. The last \ or / of the string (if any)
381 * 3. Everything after the last \ or / of the string (if any) or the whole
382 * string if there is no / or \. I believe that under Win95 this would
383 * include everything even though some might be clipped off the end whereas
384 * under Win98 that might be ellipsified too.
385 * Yet to be investigated is whether this would include wordbreaking if the
386 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
387 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
388 * broken within words).
389 * 4. All the stuff before the / or \, which is placed before the ellipsis.
391 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
392 unsigned int *len_str
, int width
, SIZE
*size
,
393 WCHAR
*modstr
, ellipsis_data
*pellip
)
398 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
400 len_ellipsis
= wcslen (ELLIPSISW
);
402 len_ellipsis
= strlenW (ELLIPSISW
);
404 if (!max_len
) return;
405 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
406 if (*len_str
+ len_ellipsis
>= max_len
)
407 *len_str
= max_len
- len_ellipsis
-1;
408 /* Hopefully this will never happen, otherwise it would probably lose
409 * the wrong character
411 str
[*len_str
] = '\0'; /* to simplify things */
413 lastBkSlash
= wcsrchr (str
, BACK_SLASH
);
414 lastFwdSlash
= wcsrchr (str
, FORWARD_SLASH
);
416 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
417 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
419 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
420 if (!lastSlash
) lastSlash
= str
;
421 len_trailing
= *len_str
- (lastSlash
- str
);
423 /* overlap-safe movement to the right */
424 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
425 memcpy (lastSlash
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
426 len_trailing
+= len_ellipsis
;
427 /* From this point on lastSlash actually points to the ellipsis in front
428 * of the last slash and len_trailing includes the ellipsis
435 if (!GreGetTextExtentExW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
436 NULL
, NULL
, size
, 0)) break;
438 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
439 NULL
, NULL
, size
)) break;
441 if (lastSlash
== str
|| size
->cx
<= width
) break;
443 /* overlap-safe movement to the left */
444 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
451 pellip
->before
= lastSlash
-str
;
452 pellip
->len
= len_ellipsis
;
453 pellip
->under
= len_under
;
454 pellip
->after
= len_trailing
- len_ellipsis
;
455 *len_str
+= len_ellipsis
;
459 memcpy(modstr
, str
, *len_str
* sizeof(WCHAR
));
460 modstr
[*len_str
] = '\0';
464 /* Check the character is Chinese, Japanese, Korean and/or Thai */
465 inline BOOL
IsCJKT(WCHAR wch
)
467 if (0x0E00 <= wch
&& wch
<= 0x0E7F)
468 return TRUE
; /* Thai */
470 if (0x3000 <= wch
&& wch
<= 0x9FFF)
471 return TRUE
; /* CJK */
473 if (0xAC00 <= wch
&& wch
<= 0xD7FF)
474 return TRUE
; /* Korean */
476 if (0xFF00 <= wch
&& wch
<= 0xFFEF)
477 return TRUE
; /* CJK */
482 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
483 static const WCHAR KinsokuClassA
[] =
485 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
486 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
487 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
488 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
489 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
490 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
491 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
492 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
493 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
496 /*********************************************************************
497 * TEXT_WordBreak (static)
499 * Perform wordbreak processing on the given string
501 * Assumes that DT_WORDBREAK has been specified and not all the characters
502 * fit. Note that this function should even be called when the first character
503 * that doesn't fit is known to be a space or tab, so that it can swallow them.
505 * Note that the Windows processing has some strange properties.
506 * 1. If the text is left-justified and there is room for some of the spaces
507 * that follow the last word on the line then those that fit are included on
509 * 2. If the text is centred or right-justified and there is room for some of
510 * the spaces that follow the last word on the line then all but one of those
511 * that fit are included on the line.
512 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
513 * character of a new line it will be skipped.
516 * hdc [in] The handle to the DC that defines the font.
517 * str [in/out] The string that needs to be broken.
518 * max_str [in] The dimension of str (number of WCHAR).
519 * len_str [in/out] The number of characters in str
520 * width [in] The maximum width permitted
521 * format [in] The format flags in effect
522 * chars_fit [in] The maximum number of characters of str that are already
523 * known to fit; chars_fit+1 is known not to fit.
524 * chars_used [out] The number of characters of str that have been "used" and
525 * do not need to be included in later text. For example this will
526 * include any spaces that have been discarded from the start of
528 * size [out] The size of the returned text in logical coordinates
530 * Pedantic assumption - Assumes that the text length is monotonically
531 * increasing with number of characters (i.e. no weird kernings)
535 * Work back from the last character that did fit to either a space or the last
536 * character of a word, whichever is met first.
537 * If there was one or the first character didn't fit then
538 * If the text is centred or right justified and that one character was a
539 * space then break the line before that character
540 * Otherwise break the line after that character
541 * and if the next character is a space then discard it.
542 * Suppose there was none (and the first character did fit).
543 * If Break Within Word is permitted
544 * break the word after the last character that fits (there must be
545 * at least one; none is caught earlier).
547 * discard any trailing space.
548 * include the whole word; it may be ellipsified later
550 * Break Within Word is permitted under a set of circumstances that are not
551 * totally clear yet. Currently our best guess is:
552 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
553 * DT_PATH_ELLIPSIS is
556 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
557 unsigned int *len_str
,
558 int width
, int format
, unsigned int chars_fit
,
559 unsigned int *chars_used
, SIZE
*size
)
563 assert (format
& DT_WORDBREAK
);
564 assert (chars_fit
< *len_str
);
566 /* Work back from the last character that did fit to either a space or the
567 * last character of a word, whichever is met first.
569 p
= str
+ chars_fit
; /* The character that doesn't fit */
573 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
574 p
--; /* the word just fitted */
577 while (p
> str
&& *(--p
) != SPACE
&& (!IsCJKT(p
[1]) ||
578 p
[1] == L
'\0' || wcschr(KinsokuClassA
, p
[1]) != NULL
))
580 word_fits
= (p
!= str
|| *p
== SPACE
|| IsCJKT(p
[1]));
582 /* If there was one. */
586 /* break the line before/after that character */
587 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
589 next_is_space
= (unsigned int) (p
- str
) < *len_str
&& *p
== SPACE
;
591 /* and if the next character is a space then discard it. */
592 *chars_used
= *len_str
;
596 /* Suppose there was none. */
599 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
602 /* break the word after the last character that fits (there must be
606 *len_str
= chars_fit
;
607 *chars_used
= chars_fit
;
609 /* FIXME - possible error. Since the next character is now removed
610 * this could make the text longer so that it no longer fits, and
611 * so we need a loop to test and shrink.
617 /* discard any trailing space. */
618 const WCHAR
*e
= str
+ *len_str
;
620 while (p
< e
&& *p
!= SPACE
)
622 *chars_used
= p
- str
;
623 if (p
< e
) /* i.e. loop failed because *p == SPACE */
626 /* include the whole word; it may be ellipsified later */
628 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
629 * so that it will be too long
633 /* Remeasure the string */
635 GreGetTextExtentExW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
, 0);
637 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
641 /*********************************************************************
644 * Skip over the given number of characters, bearing in mind prefix
645 * substitution and the fact that a character may take more than one
646 * WCHAR (Unicode surrogates are two words long) (and there may have been
650 * new_count [out] The updated count
651 * new_str [out] The updated pointer
652 * start_count [in] The count of remaining characters corresponding to the
653 * start of the string
654 * start_str [in] The starting point of the string
655 * max [in] The number of characters actually in this segment of the
656 * string (the & counts)
657 * n [in] The number of characters to skip (if prefix then
659 * prefix [in] Apply prefix substitution
665 * There must be at least n characters in the string
666 * We need max because the "line" may have ended with a & followed by a tab
667 * or newline etc. which we don't want to swallow
670 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
671 int start_count
, const WCHAR
*start_str
,
672 int max
, int n
, int prefix
)
674 /* This is specific to wide characters, MSDN doesn't say anything much
675 * about Unicode surrogates yet and it isn't clear if _wcsinc will
676 * correctly handle them so we'll just do this the easy way for now
681 const WCHAR
*str_on_entry
= start_str
;
686 if ((*start_str
== PREFIX
|| *start_str
== ALPHA_PREFIX
) && max
--)
690 start_count
-= (start_str
- str_on_entry
);
697 *new_str
= start_str
;
698 *new_count
= start_count
;
701 /*********************************************************************
704 * Reanalyse the text to find the prefixed character. This is called when
705 * wordbreaking or ellipsification has shortened the string such that the
706 * previously noted prefixed character is no longer visible.
709 * str [in] The original string segment (including all characters)
710 * ns [in] The number of characters in str (including prefixes)
711 * pe [in] The ellipsification data
714 * The prefix offset within the new string segment (the one that contains the
715 * ellipses and does not contain the prefix characters) (-1 if none)
718 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int ns
,
719 const ellipsis_data
*pe
)
723 unsigned int n
= pe
->before
+ pe
->under
+ pe
->after
;
725 for (i
= 0; i
< n
; i
++, str
++)
727 if (i
== (unsigned int) pe
->before
)
729 /* Reached the path ellipsis; jump over it */
730 if (ns
< (unsigned int) pe
->under
) break;
734 if (!pe
->after
) break; /* Nothing after the path ellipsis */
738 if (*str
++ == PREFIX
|| *str
== ALPHA_PREFIX
)
743 result
= (i
< (unsigned int) pe
->before
|| pe
->under
== 0) ? i
: i
- pe
->under
+ pe
->len
;
744 /* pe->len may be non-zero while pe_under is zero */
751 /*********************************************************************
752 * Returns true if and only if the remainder of the line is a single
753 * newline representation or nothing
756 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
758 if (!num_chars
) return TRUE
;
759 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
760 if (!--num_chars
) return TRUE
;
761 if (*str
== *(str
+1)) return FALSE
;
763 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
764 if (--num_chars
) return FALSE
;
768 /*********************************************************************
769 * Return next line of text from a string.
771 * hdc - handle to DC.
772 * str - string to parse into lines.
773 * count - length of str.
774 * dest - destination in which to return line.
775 * len - dest buffer size in chars on input, copied length into dest on output.
776 * width - maximum width of line in pixels.
777 * format - format type passed to DrawText.
778 * retsize - returned size of the line in pixels.
779 * last_line - TRUE if is the last line that will be processed
780 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
781 * the return string is built.
782 * tabwidth - The width of a tab in logical coordinates
783 * pprefix_offset - Here is where we return the offset within dest of the first
784 * prefixed (underlined) character. -1 is returned if there
785 * are none. Note that there may be more; the calling code
786 * will need to use TEXT_Reprefix to find any later ones.
787 * pellip - Here is where we return the information about any ellipsification
788 * that was carried out. Note that if tabs are being expanded then
789 * this data will correspond to the last text segment actually
790 * returned in dest; by definition there would not have been any
791 * ellipsification in earlier text segments of the line.
793 * Returns pointer to next char in str after end of the line
794 * or NULL if end of str reached.
796 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
797 WCHAR
*dest
, int *len
, int width
, DWORD format
,
798 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
,
799 int tabwidth
, int *pprefix_offset
,
800 ellipsis_data
*pellip
)
806 int seg_i
, seg_count
, seg_j
;
811 unsigned int j_in_seg
;
813 *pprefix_offset
= -1;
815 /* For each text segment in the line */
821 /* Skip any leading tabs */
823 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
825 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
826 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
827 while (*count
&& str
[i
] == TAB
)
830 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
835 /* Now copy as far as the next tab or cr/lf or eos */
842 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
843 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
845 if ((format
& DT_NOPREFIX
) || *count
<= 1)
847 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
851 if (str
[i
] == PREFIX
|| str
[i
] == ALPHA_PREFIX
) {
852 (*count
)--, i
++; /* Throw away the prefix itself */
853 if (str
[i
] == PREFIX
)
855 /* Swallow it before we see it again */
856 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
858 else if (*pprefix_offset
== -1 || *pprefix_offset
>= seg_j
)
862 /* else the previous prefix was in an earlier segment of the
863 * line; we will leave it to the drawing code to catch this
867 else if (str
[i
] == KANA_PREFIX
)
869 /* Throw away katakana access keys */
870 (*count
)--, i
++; /* skip the prefix */
871 (*count
)--, i
++; /* skip the letter */
875 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
880 /* Measure the whole text segment and possibly WordBreak and
884 j_in_seg
= j
- seg_j
;
885 max_seg_width
= width
- plen
;
887 GreGetTextExtentExW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, (PULONG
)&num_fit
, NULL
, &size
, 0);
889 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
892 /* The Microsoft handling of various combinations of formats is weird.
893 * The following may very easily be incorrect if several formats are
894 * combined, and may differ between versions (to say nothing of the
895 * several bugs in the Microsoft versions).
898 line_fits
= (num_fit
>= j_in_seg
);
899 if (!line_fits
&& (format
& DT_WORDBREAK
))
902 unsigned int chars_used
;
903 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
904 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
905 line_fits
= (size
.cx
<= max_seg_width
);
906 /* and correct the counts */
907 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
908 chars_used
, !(format
& DT_NOPREFIX
));
912 pellip
->before
= j_in_seg
;
917 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
919 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
920 max_seg_width
, &size
, *p_retstr
, pellip
);
921 line_fits
= (size
.cx
<= max_seg_width
);
924 /* NB we may end up ellipsifying a word-broken or path_ellipsified
926 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
927 ((format
& DT_END_ELLIPSIS
) &&
928 ((last_line
&& *count
) ||
929 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
931 int before
, len_ellipsis
;
932 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
933 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
934 if (before
> pellip
->before
)
936 /* We must have done a path ellipsis too */
937 pellip
->after
= before
- pellip
->before
- pellip
->len
;
938 /* Leave the len as the length of the first ellipsis */
942 /* If we are here after a path ellipsification it must be
943 * because even the ellipsis itself didn't fit.
945 assert (pellip
->under
== 0 && pellip
->after
== 0);
946 pellip
->before
= before
;
947 pellip
->len
= len_ellipsis
;
948 /* pellip->after remains as zero as does
952 line_fits
= (size
.cx
<= max_seg_width
);
955 /* As an optimisation if we have ellipsified and we are expanding
956 * tabs and we haven't reached the end of the line we can skip to it
957 * now rather than going around the loop again.
959 if ((format
& DT_EXPANDTABS
) && ellipsified
)
961 if (format
& DT_SINGLELINE
)
965 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
972 j
= seg_j
+ j_in_seg
;
973 if (*pprefix_offset
>= seg_j
+ pellip
->before
)
975 *pprefix_offset
= TEXT_Reprefix (str
+ seg_i
, i
- seg_i
, pellip
);
976 if (*pprefix_offset
!= -1)
977 *pprefix_offset
+= seg_j
;
981 if (size
.cy
> retsize
->cy
)
982 retsize
->cy
= size
.cy
;
988 else if (str
[i
] == CR
|| str
[i
] == LF
)
991 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
997 /* else it was a Tab and we go around again */
1009 /***********************************************************************
1010 * TEXT_DrawUnderscore
1012 * Draw the underline under the prefixed character
1015 * hdc [in] The handle of the DC for drawing
1016 * x [in] The x location of the line segment (logical coordinates)
1017 * y [in] The y location of where the underscore should appear
1018 * (logical coordinates)
1019 * str [in] The text of the line segment
1020 * offset [in] The offset of the underscored character within str
1021 * rect [in] Clipping rectangle (if not NULL)
1023 /* Synced with wine 1.1.32 */
1024 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
, const RECT
*rect
)
1032 GreGetTextExtentW (hdc
, str
, offset
, &size
, 0);
1034 GetTextExtentPointW (hdc
, str
, offset
, &size
);
1036 prefix_x
= x
+ size
.cx
;
1038 GreGetTextExtentW (hdc
, str
, offset
+1, &size
, 0);
1040 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
1042 prefix_end
= x
+ size
.cx
- 1;
1043 /* The above method may eventually be slightly wrong due to kerning etc. */
1045 /* Check for clipping */
1048 if (prefix_x
> rect
->right
|| prefix_end
< rect
->left
||
1049 y
< rect
->top
|| y
> rect
->bottom
)
1050 return; /* Completely outside */
1051 /* Partially outside */
1052 if (prefix_x
< rect
->left
) prefix_x
= rect
->left
;
1053 if (prefix_end
> rect
->right
) prefix_end
= rect
->right
;
1056 hpen
= NtGdiCreatePen (PS_SOLID
, 1, GreGetTextColor (hdc
), NULL
);
1057 oldPen
= NtGdiSelectPen (hdc
, hpen
);
1058 GreMoveTo (hdc
, prefix_x
, y
, NULL
);
1059 NtGdiLineTo (hdc
, prefix_end
, y
);
1060 NtGdiSelectPen (hdc
, oldPen
);
1061 GreDeleteObject (hpen
);
1063 hpen
= CreatePen (PS_SOLID
, 1, GetTextColor (hdc
));
1064 oldPen
= SelectObject (hdc
, hpen
);
1065 MoveToEx (hdc
, prefix_x
, y
, NULL
);
1066 LineTo (hdc
, prefix_end
, y
);
1067 SelectObject (hdc
, oldPen
);
1068 DeleteObject (hpen
);
1072 /***********************************************************************
1073 * DrawTextExW (USER32.@)
1075 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1076 * is not quite complete, especially with regard to \0. We will assume that
1077 * the returned string could have a length of up to i_count+3 and also have
1078 * a trailing \0 (which would be 4 more than a not-null-terminated string but
1079 * 3 more than a null-terminated string). If this is not so then increase
1080 * the allowance in DrawTextExA.
1082 #define MAX_BUFFER 1024
1086 * Synced with Wine Staging 1.7.37
1088 INT WINAPI
DrawTextExWorker( HDC hdc
,
1093 LPDRAWTEXTPARAMS dtp
)
1096 const WCHAR
*strPtr
;
1097 WCHAR
*retstr
, *p_retstr
;
1099 WCHAR line
[MAX_BUFFER
];
1100 int len
, lh
, count
=i_count
;
1102 int lmargin
= 0, rmargin
= 0;
1103 int x
= rect
->left
, y
= rect
->top
;
1104 int width
= rect
->right
- rect
->left
;
1107 int tabwidth
/* to keep gcc happy */ = 0;
1109 ellipsis_data ellip
;
1110 BOOL invert_y
=FALSE
;
1112 TRACE("%S, %d, %08x\n", str
, count
, flags
);
1114 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str
, count
), count
,
1115 wine_dbgstr_rect(rect
), flags
);
1117 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1118 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
1124 if (flags
& DT_SINGLELINE
)
1125 flags
&= ~DT_WORDBREAK
;
1127 GreGetTextMetricsW(hdc
, &tm
);
1129 GetTextMetricsW(hdc
, &tm
);
1131 if (flags
& DT_EXTERNALLEADING
)
1132 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1136 if (str
[0] && count
== 0)
1139 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1142 if (GreGetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1144 SIZE window_ext
, viewport_ext
;
1145 GreGetWindowExtEx(hdc
, &window_ext
);
1146 GreGetViewportExtEx(hdc
, &viewport_ext
);
1147 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1151 if (GetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1153 SIZE window_ext
, viewport_ext
;
1154 GetWindowExtEx(hdc
, &window_ext
);
1155 GetViewportExtEx(hdc
, &viewport_ext
);
1156 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1163 count
= wcslen(str
);
1165 count
= strlenW(str
);
1169 if( flags
& DT_CALCRECT
)
1171 rect
->right
= rect
->left
;
1172 if( flags
& DT_SINGLELINE
)
1173 rect
->bottom
= rect
->top
+ (invert_y
? -lh
: lh
);
1175 rect
->bottom
= rect
->top
;
1183 lmargin
= dtp
->iLeftMargin
;
1184 rmargin
= dtp
->iRightMargin
;
1185 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
1187 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
1190 if (flags
& DT_EXPANDTABS
)
1192 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
) ? dtp
->iTabLength
: 8;
1193 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
1196 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
1198 if (flags
& DT_MODIFYSTRING
)
1200 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
1202 retstr
= ExAllocatePoolWithTag(PagedPool
, size_retstr
, USERTAG_RTL
);
1204 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
1206 if (!retstr
) return 0;
1207 memcpy (retstr
, str
, size_retstr
);
1218 len
= sizeof(line
)/sizeof(line
[0]);
1220 last_line
= !(flags
& DT_NOCLIP
) && y
- ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) < rect
->bottom
;
1222 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
1223 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
, tabwidth
, &prefix_offset
, &ellip
);
1226 if (flags
& DT_CENTER
)
1228 if (((rect
->right
- rect
->left
) < size
.cx
) && (flags
& DT_CALCRECT
))
1230 x
= rect
->left
+ size
.cx
;
1234 x
= (rect
->left
+ rect
->right
- size
.cx
) / 2;
1238 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
1241 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
1243 if (flags
& DT_SINGLELINE
)
1245 if (flags
& DT_VCENTER
)
1248 if (((rect
->bottom
- rect
->top
) < (invert_y
? -size
.cy
: size
.cy
)) && (flags
& DT_CALCRECT
))
1250 y
= rect
->top
+ (invert_y
? -size
.cy
: size
.cy
);
1255 y
= rect
->top
+ (rect
->bottom
- rect
->top
) / 2 + (invert_y
? (size
.cy
/ 2) : (-size
.cy
/ 2));
1260 else if (flags
& DT_BOTTOM
)
1261 y
= rect
->bottom
+ (invert_y
? 0 : -size
.cy
);
1264 if (!(flags
& DT_CALCRECT
))
1266 const WCHAR
*str
= line
;
1272 if ((flags
& DT_EXPANDTABS
))
1275 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
1277 if (len_seg
!= len
&&
1279 !GreGetTextExtentW(hdc
, str
, len_seg
, &size
, 0))
1281 !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
1285 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1287 HeapFree (GetProcessHeap(), 0, retstr
);
1295 if (!GreExtTextOutW( hdc
, xseg
, y
,
1296 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1297 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1298 rect
, str
, len_seg
, NULL
, 0 ))
1300 if (!ExtTextOutW( hdc
, xseg
, y
,
1301 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1302 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1303 rect
, str
, len_seg
, NULL
))
1307 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1309 HeapFree (GetProcessHeap(), 0, retstr
);
1313 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
1315 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
, (flags
& DT_NOCLIP
) ? NULL
: rect
);
1321 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
1323 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
1324 if (prefix_offset
!= -1)
1326 if (prefix_offset
< len_seg
)
1328 /* We have just drawn an underscore; we ought to
1329 * figure out where the next one is. I am going
1330 * to leave it for now until I have a better model
1331 * for the line, which will make reprefixing easier.
1332 * This is where ellip would be used.
1337 prefix_offset
-= len_seg
;
1342 else if (size
.cx
> max_width
)
1343 max_width
= size
.cx
;
1345 y
+= invert_y
? -lh
: lh
;
1347 dtp
->uiLengthDrawn
+= len
;
1349 while (strPtr
&& !last_line
);
1351 if (flags
& DT_CALCRECT
)
1353 rect
->right
= rect
->left
+ max_width
;
1356 rect
->right
+= lmargin
+ rmargin
;
1360 memcpy (str
, retstr
, size_retstr
);
1362 ExFreePoolWithTag(retstr
, USERTAG_RTL
);
1364 HeapFree (GetProcessHeap(), 0, retstr
);
1367 return y
- rect
->top
;