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.
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/input.c
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * 09-05-2001 CSH Created
29 /* INCLUDES ******************************************************************/
33 #include <wine/debug.h>
35 WINE_DEFAULT_DEBUG_CHANNEL(text
);
37 /* FUNCTIONS *****************************************************************/
45 #define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__))
49 void _font_assert(const char *msg
, const char *file
, int line
)
51 /* Assertion failed at foo.c line 45: x<y */
52 DbgPrint("Assertion failed at %s line %d: %s\n", file
, line
, msg
);
54 for(;;); /* eliminate warning by mingw */
69 CONST LPINT lpnTabStopPositions
,
76 len
= MultiByteToWideChar(CP_ACP
, 0, lpString
, nCount
, NULL
, 0);
77 strW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
82 MultiByteToWideChar(CP_ACP
, 0, lpString
, nCount
, strW
, len
);
83 ret
= TabbedTextOutW(hDC
, X
, Y
, strW
, len
, nTabPositions
, lpnTabStopPositions
, nTabOrigin
);
84 HeapFree(GetProcessHeap(), 0, strW
);
89 /***********************************************************************
92 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
93 * Note: this doesn't work too well for text-alignment modes other
94 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
96 /* WINE synced 22-May-2006 */
97 static LONG
TEXT_TabbedTextOut( HDC hdc
, INT x
, INT y
, LPCWSTR lpstr
,
98 INT count
, INT cTabStops
, const INT
*lpTabPos
, INT nTabOrg
,
111 defWidth
= *lpTabPos
;
117 GetTextMetricsA( hdc
, &tm
);
118 defWidth
= 8 * tm
.tmAveCharWidth
;
127 /* chop the string into substrings of 0 or more <tabs>
128 * possibly followed by 1 or more normal characters */
129 for (i
= 0; i
< count
; i
++)
130 if (lpstr
[i
] != '\t') break;
131 for (j
= i
; j
< count
; j
++)
132 if (lpstr
[j
] == '\t') break;
133 /* get the extent of the normal character part */
134 GetTextExtentPointW( hdc
, lpstr
+ i
, j
- i
, &extent
);
135 /* and if there is a <tab>, calculate its position */
137 /* get x coordinate for the drawing of this string */
138 for (; cTabStops
> i
; lpTabPos
++, cTabStops
--)
140 if( nTabOrg
+ abs( *lpTabPos
) > x
) {
141 if( lpTabPos
[ i
- 1] >= 0) {
142 /* a left aligned tab */
143 x
= nTabOrg
+ lpTabPos
[ i
-1] + extent
.cx
;
148 /* if tab pos is negative then text is right-aligned
149 * to tab stop meaning that the string extends to the
150 * left, so we must subtract the width of the string */
151 if (nTabOrg
- lpTabPos
[ i
- 1] - extent
.cx
> x
)
153 x
= nTabOrg
- lpTabPos
[ i
- 1];
160 /* if we have run out of tab stops and we have a valid default tab
161 * stop width then round x up to that width */
162 if ((cTabStops
<= i
) && (defWidth
> 0)) {
163 x0
= nTabOrg
+ ((x
- nTabOrg
) / defWidth
+ i
) * defWidth
;
165 } else if ((cTabStops
<= i
) && (defWidth
< 0)) {
166 x
= nTabOrg
+ ((x
- nTabOrg
+ extent
.cx
) / -defWidth
+ i
)
177 r
.bottom
= y
+ extent
.cy
;
178 ExtTextOutW( hdc
, x0
, y
, GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
179 &r
, lpstr
+ i
, j
- i
, NULL
);
184 return MAKELONG(x
- start
, extent
.cy
);
199 CONST LPINT lpnTabStopPositions
,
202 return TEXT_TabbedTextOut(hDC
, X
, Y
, lpString
, nCount
, nTabPositions
, lpnTabStopPositions
, nTabOrigin
, TRUE
);
205 /* WINE synced 22-May-2006 */
211 GetTabbedTextExtentA(
216 CONST LPINT lpnTabStopPositions
)
219 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, lpString
, nCount
, NULL
, 0);
220 LPWSTR strW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
224 MultiByteToWideChar(CP_ACP
, 0, lpString
, nCount
, strW
, len
);
226 ret
= GetTabbedTextExtentW(hDC
, strW
, len
, nTabPositions
,
227 lpnTabStopPositions
);
229 HeapFree(GetProcessHeap(), 0, strW
);
239 GetTabbedTextExtentW(
244 CONST LPINT lpnTabStopPositions
)
246 return TEXT_TabbedTextOut(hDC
, 0, 0, lpString
, nCount
, nTabPositions
, lpnTabStopPositions
, 0, FALSE
);
249 /*********************************************************************
254 * Copyright 1993, 1994 Alexandre Julliard
255 * Copyright 2002 Bill Medland
258 * How many buffers to use
259 * While processing in DrawText there are potentially three different forms
260 * of the text that need to be held. How are they best held?
261 * 1. The original text is needed, of course, to see what to display.
262 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
264 * 3. The buffered text that is about to be displayed e.g. the current line.
265 * Typically this will exclude the ampersands used for prefixing etc.
268 * a. If the buffered text to be displayed includes the ampersands then
269 * we will need special measurement and draw functions that will ignore
270 * the ampersands (e.g. by copying to a buffer without the prefix and
271 * then using the normal forms). This may involve less space but may
272 * require more processing. e.g. since a line containing tabs may
273 * contain several underlined characters either we need to carry around
274 * a list of prefix locations or we may need to locate them several
276 * b. If we actually directly modify the "original text" as we go then we
277 * will need some special "caching" to handle the fact that when we
278 * ellipsify the text the ellipsis may modify the next line of text,
279 * which we have not yet processed. (e.g. ellipsification of a W at the
280 * end of a line will overwrite the W, the \n and the first character of
281 * the next line, and a \0 will overwrite the second. Try it!!)
283 * Option 1. Three separate storages. (To be implemented)
284 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
285 * the edited string in some form, either as the string itself or as some
286 * sort of "edit list" to be applied just before returning.
287 * Use a buffer that holds the ellipsified current line sans ampersands
288 * and accept the need occasionally to recalculate the prefixes (if
289 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
298 #define FORWARD_SLASH '/'
299 #define BACK_SLASH '\\'
301 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
303 typedef struct tag_ellipsis_data
311 /*********************************************************************
312 * TEXT_Ellipsify (static)
314 * Add an ellipsis to the end of the given string whilst ensuring it fits.
316 * If the ellipsis alone doesn't fit then it will be returned anyway.
318 * See Also TEXT_PathEllipsify
321 * hdc [in] The handle to the DC that defines the font.
322 * str [in/out] The string that needs to be modified.
323 * max_str [in] The dimension of str (number of WCHAR).
324 * len_str [in/out] The number of characters in str
325 * width [in] The maximum width permitted (in logical coordinates)
326 * size [out] The dimensions of the text
327 * modstr [out] The modified form of the string, to be returned to the
328 * calling program. It is assumed that the caller has
329 * made sufficient space available so we don't need to
330 * know the size of the space. This pointer may be NULL if
331 * the modified string is not required.
332 * len_before [out] The number of characters before the ellipsis.
333 * len_ellip [out] The number of characters in the ellipsis.
335 * See for example Microsoft article Q249678.
337 * For now we will simply use three dots rather than worrying about whether
338 * the font contains an explicit ellipsis character.
340 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
341 unsigned int *len_str
, int width
, SIZE
*size
,
343 int *len_before
, int *len_ellip
)
345 unsigned int len_ellipsis
;
346 unsigned int lo
, mid
, hi
;
348 len_ellipsis
= strlenW (ELLIPSISW
);
349 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
350 if (*len_str
> max_len
- len_ellipsis
)
351 *len_str
= max_len
- len_ellipsis
;
353 /* First do a quick binary search to get an upper bound for *len_str. */
355 GetTextExtentExPointW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
) &&
358 for (lo
= 0, hi
= *len_str
; lo
< hi
; )
361 if (!GetTextExtentExPointW(hdc
, str
, mid
, width
, NULL
, NULL
, size
))
363 if (size
->cx
> width
)
370 /* Now this should take only a couple iterations at most. */
373 memcpy(str
+ *len_str
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
375 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
376 NULL
, NULL
, size
)) break;
378 if (!*len_str
|| size
->cx
<= width
) break;
382 *len_ellip
= len_ellipsis
;
383 *len_before
= *len_str
;
384 *len_str
+= len_ellipsis
;
388 memcpy (modstr
, str
, *len_str
* sizeof(WCHAR
));
389 modstr
[*len_str
] = '\0';
393 /*********************************************************************
394 * TEXT_PathEllipsify (static)
396 * Add an ellipsis to the provided string in order to make it fit within
397 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
400 * See Also TEXT_Ellipsify
403 * hdc [in] The handle to the DC that defines the font.
404 * str [in/out] The string that needs to be modified
405 * max_str [in] The dimension of str (number of WCHAR).
406 * len_str [in/out] The number of characters in str
407 * width [in] The maximum width permitted (in logical coordinates)
408 * size [out] The dimensions of the text
409 * modstr [out] The modified form of the string, to be returned to the
410 * calling program. It is assumed that the caller has
411 * made sufficient space available so we don't need to
412 * know the size of the space. This pointer may be NULL if
413 * the modified string is not required.
414 * pellip [out] The ellipsification results
416 * For now we will simply use three dots rather than worrying about whether
417 * the font contains an explicit ellipsis character.
419 * The following applies, I think to Win95. We will need to extend it for
420 * Win98 which can have both path and end ellipsis at the same time (e.g.
421 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
423 * The resulting string consists of as much as possible of the following:
424 * 1. The ellipsis itself
425 * 2. The last \ or / of the string (if any)
426 * 3. Everything after the last \ or / of the string (if any) or the whole
427 * string if there is no / or \. I believe that under Win95 this would
428 * include everything even though some might be clipped off the end whereas
429 * under Win98 that might be ellipsified too.
430 * Yet to be investigated is whether this would include wordbreaking if the
431 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
432 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
433 * broken within words).
434 * 4. All the stuff before the / or \, which is placed before the ellipsis.
436 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
437 unsigned int *len_str
, int width
, SIZE
*size
,
438 WCHAR
*modstr
, ellipsis_data
*pellip
)
443 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
445 len_ellipsis
= strlenW (ELLIPSISW
);
446 if (!max_len
) return;
447 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
448 if (*len_str
+ len_ellipsis
>= max_len
)
449 *len_str
= max_len
- len_ellipsis
-1;
450 /* Hopefully this will never happen, otherwise it would probably lose
451 * the wrong character
453 str
[*len_str
] = '\0'; /* to simplify things */
455 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
456 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
457 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
458 if (!lastSlash
) lastSlash
= str
;
459 len_trailing
= *len_str
- (lastSlash
- str
);
461 /* overlap-safe movement to the right */
462 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
463 memcpy (lastSlash
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
464 len_trailing
+= len_ellipsis
;
465 /* From this point on lastSlash actually points to the ellipsis in front
466 * of the last slash and len_trailing includes the ellipsis
472 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
473 NULL
, NULL
, size
)) break;
475 if (lastSlash
== str
|| size
->cx
<= width
) break;
477 /* overlap-safe movement to the left */
478 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
485 pellip
->before
= lastSlash
-str
;
486 pellip
->len
= len_ellipsis
;
487 pellip
->under
= len_under
;
488 pellip
->after
= len_trailing
- len_ellipsis
;
489 *len_str
+= len_ellipsis
;
493 memcpy(modstr
, str
, *len_str
* sizeof(WCHAR
));
494 modstr
[*len_str
] = '\0';
498 /* Check the character is Chinese, Japanese, Korean and/or Thai */
499 inline BOOL
IsCJKT(WCHAR wch
)
501 if (0x0E00 <= wch
&& wch
<= 0x0E7F)
502 return TRUE
; /* Thai */
504 if (0x3000 <= wch
&& wch
<= 0x9FFF)
505 return TRUE
; /* CJK */
507 if (0xAC00 <= wch
&& wch
<= 0xD7FF)
508 return TRUE
; /* Korean */
510 if (0xFF00 <= wch
&& wch
<= 0xFFEF)
511 return TRUE
; /* CJK */
516 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
517 static const WCHAR KinsokuClassA
[] =
519 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
520 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
521 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
522 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
523 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
524 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
525 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
526 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
527 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
530 /*********************************************************************
531 * TEXT_WordBreak (static)
533 * Perform wordbreak processing on the given string
535 * Assumes that DT_WORDBREAK has been specified and not all the characters
536 * fit. Note that this function should even be called when the first character
537 * that doesn't fit is known to be a space or tab, so that it can swallow them.
539 * Note that the Windows processing has some strange properties.
540 * 1. If the text is left-justified and there is room for some of the spaces
541 * that follow the last word on the line then those that fit are included on
543 * 2. If the text is centred or right-justified and there is room for some of
544 * the spaces that follow the last word on the line then all but one of those
545 * that fit are included on the line.
546 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
547 * character of a new line it will be skipped.
550 * hdc [in] The handle to the DC that defines the font.
551 * str [in/out] The string that needs to be broken.
552 * max_str [in] The dimension of str (number of WCHAR).
553 * len_str [in/out] The number of characters in str
554 * width [in] The maximum width permitted
555 * format [in] The format flags in effect
556 * chars_fit [in] The maximum number of characters of str that are already
557 * known to fit; chars_fit+1 is known not to fit.
558 * chars_used [out] The number of characters of str that have been "used" and
559 * do not need to be included in later text. For example this will
560 * include any spaces that have been discarded from the start of
562 * size [out] The size of the returned text in logical coordinates
564 * Pedantic assumption - Assumes that the text length is monotonically
565 * increasing with number of characters (i.e. no weird kernings)
569 * Work back from the last character that did fit to either a space or the last
570 * character of a word, whichever is met first.
571 * If there was one or the first character didn't fit then
572 * If the text is centred or right justified and that one character was a
573 * space then break the line before that character
574 * Otherwise break the line after that character
575 * and if the next character is a space then discard it.
576 * Suppose there was none (and the first character did fit).
577 * If Break Within Word is permitted
578 * break the word after the last character that fits (there must be
579 * at least one; none is caught earlier).
581 * discard any trailing space.
582 * include the whole word; it may be ellipsified later
584 * Break Within Word is permitted under a set of circumstances that are not
585 * totally clear yet. Currently our best guess is:
586 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
587 * DT_PATH_ELLIPSIS is
590 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
591 unsigned int *len_str
,
592 int width
, int format
, unsigned int chars_fit
,
593 unsigned int *chars_used
, SIZE
*size
)
597 assert (format
& DT_WORDBREAK
);
598 assert (chars_fit
< *len_str
);
600 /* Work back from the last character that did fit to either a space or the
601 * last character of a word, whichever is met first.
603 p
= str
+ chars_fit
; /* The character that doesn't fit */
606 ; /* we pretend that it fits anyway */
607 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
608 p
--; /* the word just fitted */
611 while (p
> str
&& *(--p
) != SPACE
&& (!IsCJKT(p
[1]) ||
612 p
[1] == L
'\0' || wcschr(KinsokuClassA
, p
[1]) != NULL
))
614 word_fits
= (p
!= str
|| *p
== SPACE
|| IsCJKT(p
[1]));
616 /* If there was one or the first character didn't fit then */
620 /* break the line before/after that character */
621 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
623 next_is_space
= (unsigned int) (p
- str
) < *len_str
&& *p
== SPACE
;
625 /* and if the next character is a space then discard it. */
626 *chars_used
= *len_str
;
630 /* Suppose there was none. */
633 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
636 /* break the word after the last character that fits (there must be
637 * at least one; none is caught earlier).
639 *len_str
= chars_fit
;
640 *chars_used
= chars_fit
;
642 /* FIXME - possible error. Since the next character is now removed
643 * this could make the text longer so that it no longer fits, and
644 * so we need a loop to test and shrink.
650 /* discard any trailing space. */
651 const WCHAR
*e
= str
+ *len_str
;
653 while (p
< e
&& *p
!= SPACE
)
655 *chars_used
= p
- str
;
656 if (p
< e
) /* i.e. loop failed because *p == SPACE */
659 /* include the whole word; it may be ellipsified later */
661 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
662 * so that it will be too long
666 /* Remeasure the string */
667 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
670 /*********************************************************************
673 * Skip over the given number of characters, bearing in mind prefix
674 * substitution and the fact that a character may take more than one
675 * WCHAR (Unicode surrogates are two words long) (and there may have been
679 * new_count [out] The updated count
680 * new_str [out] The updated pointer
681 * start_count [in] The count of remaining characters corresponding to the
682 * start of the string
683 * start_str [in] The starting point of the string
684 * max [in] The number of characters actually in this segment of the
685 * string (the & counts)
686 * n [in] The number of characters to skip (if prefix then
688 * prefix [in] Apply prefix substitution
694 * There must be at least n characters in the string
695 * We need max because the "line" may have ended with a & followed by a tab
696 * or newline etc. which we don't want to swallow
699 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
700 int start_count
, const WCHAR
*start_str
,
701 int max
, int n
, int prefix
)
703 /* This is specific to wide characters, MSDN doesn't say anything much
704 * about Unicode surrogates yet and it isn't clear if _wcsinc will
705 * correctly handle them so we'll just do this the easy way for now
710 const WCHAR
*str_on_entry
= start_str
;
715 if (*start_str
++ == PREFIX
&& max
--)
718 start_count
-= (start_str
- str_on_entry
);
725 *new_str
= start_str
;
726 *new_count
= start_count
;
729 /*********************************************************************
732 * Reanalyse the text to find the prefixed character. This is called when
733 * wordbreaking or ellipsification has shortened the string such that the
734 * previously noted prefixed character is no longer visible.
737 * str [in] The original string segment (including all characters)
738 * ns [in] The number of characters in str (including prefixes)
739 * pe [in] The ellipsification data
742 * The prefix offset within the new string segment (the one that contains the
743 * ellipses and does not contain the prefix characters) (-1 if none)
746 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int ns
,
747 const ellipsis_data
*pe
)
751 unsigned int n
= pe
->before
+ pe
->under
+ pe
->after
;
755 if (i
== (unsigned int) pe
->before
)
757 /* Reached the path ellipsis; jump over it */
758 if (ns
< (unsigned int) pe
->under
) break;
762 if (!pe
->after
) break; /* Nothing after the path ellipsis */
766 if (*str
++ == PREFIX
)
770 result
= (i
< (unsigned int) pe
->before
|| pe
->under
== 0) ? i
: i
- pe
->under
+ pe
->len
;
771 /* pe->len may be non-zero while pe_under is zero */
780 /*********************************************************************
781 * Returns true if and only if the remainder of the line is a single
782 * newline representation or nothing
785 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
787 if (!num_chars
) return TRUE
;
788 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
789 if (!--num_chars
) return TRUE
;
790 if (*str
== *(str
+1)) return FALSE
;
792 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
793 if (--num_chars
) return FALSE
;
797 /*********************************************************************
798 * Return next line of text from a string.
800 * hdc - handle to DC.
801 * str - string to parse into lines.
802 * count - length of str.
803 * dest - destination in which to return line.
804 * len - dest buffer size in chars on input, copied length into dest on output.
805 * width - maximum width of line in pixels.
806 * format - format type passed to DrawText.
807 * retsize - returned size of the line in pixels.
808 * last_line - TRUE if is the last line that will be processed
809 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
810 * the return string is built.
811 * tabwidth - The width of a tab in logical coordinates
812 * pprefix_offset - Here is where we return the offset within dest of the first
813 * prefixed (underlined) character. -1 is returned if there
814 * are none. Note that there may be more; the calling code
815 * will need to use TEXT_Reprefix to find any later ones.
816 * pellip - Here is where we return the information about any ellipsification
817 * that was carried out. Note that if tabs are being expanded then
818 * this data will correspond to the last text segment actually
819 * returned in dest; by definition there would not have been any
820 * ellipsification in earlier text segments of the line.
822 * Returns pointer to next char in str after end of the line
823 * or NULL if end of str reached.
825 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
826 WCHAR
*dest
, int *len
, int width
, DWORD format
,
827 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
,
828 int tabwidth
, int *pprefix_offset
,
829 ellipsis_data
*pellip
)
835 int seg_i
, seg_count
, seg_j
;
840 unsigned int j_in_seg
;
842 *pprefix_offset
= -1;
844 /* For each text segment in the line */
850 /* Skip any leading tabs */
852 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
854 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
855 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
856 while (*count
&& str
[i
] == TAB
)
859 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
864 /* Now copy as far as the next tab or cr/lf or eos */
871 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
872 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
874 if (str
[i
] == PREFIX
&& !(format
& DT_NOPREFIX
) && *count
> 1)
876 (*count
)--, i
++; /* Throw away the prefix itself */
877 if (str
[i
] == PREFIX
)
879 /* Swallow it before we see it again */
880 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
882 else if (*pprefix_offset
== -1 || *pprefix_offset
>= seg_j
)
886 /* else the previous prefix was in an earlier segment of the
887 * line; we will leave it to the drawing code to catch this
893 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
898 /* Measure the whole text segment and possibly WordBreak and
902 j_in_seg
= j
- seg_j
;
903 max_seg_width
= width
- plen
;
904 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
906 /* The Microsoft handling of various combinations of formats is weird.
907 * The following may very easily be incorrect if several formats are
908 * combined, and may differ between versions (to say nothing of the
909 * several bugs in the Microsoft versions).
912 line_fits
= (num_fit
>= j_in_seg
);
913 if (!line_fits
&& (format
& DT_WORDBREAK
))
916 unsigned int chars_used
;
917 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
918 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
919 line_fits
= (size
.cx
<= max_seg_width
);
920 /* and correct the counts */
921 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
922 chars_used
, !(format
& DT_NOPREFIX
));
926 pellip
->before
= j_in_seg
;
931 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
933 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
934 max_seg_width
, &size
, *p_retstr
, pellip
);
935 line_fits
= (size
.cx
<= max_seg_width
);
938 /* NB we may end up ellipsifying a word-broken or path_ellipsified
940 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
941 ((format
& DT_END_ELLIPSIS
) &&
942 ((last_line
&& *count
) ||
943 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
945 int before
, len_ellipsis
;
946 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
947 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
948 if (before
> pellip
->before
)
950 /* We must have done a path ellipsis too */
951 pellip
->after
= before
- pellip
->before
- pellip
->len
;
952 /* Leave the len as the length of the first ellipsis */
956 /* If we are here after a path ellipsification it must be
957 * because even the ellipsis itself didn't fit.
959 assert (pellip
->under
== 0 && pellip
->after
== 0);
960 pellip
->before
= before
;
961 pellip
->len
= len_ellipsis
;
962 /* pellip->after remains as zero as does
966 line_fits
= (size
.cx
<= max_seg_width
);
969 /* As an optimisation if we have ellipsified and we are expanding
970 * tabs and we haven't reached the end of the line we can skip to it
971 * now rather than going around the loop again.
973 if ((format
& DT_EXPANDTABS
) && ellipsified
)
975 if (format
& DT_SINGLELINE
)
979 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
986 j
= seg_j
+ j_in_seg
;
987 if (*pprefix_offset
>= seg_j
+ pellip
->before
)
989 *pprefix_offset
= TEXT_Reprefix (str
+ seg_i
, i
- seg_i
, pellip
);
990 if (*pprefix_offset
!= -1)
991 *pprefix_offset
+= seg_j
;
995 if (size
.cy
> retsize
->cy
)
996 retsize
->cy
= size
.cy
;
1002 else if (str
[i
] == CR
|| str
[i
] == LF
)
1005 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
1011 /* else it was a Tab and we go around again */
1023 /***********************************************************************
1024 * TEXT_DrawUnderscore
1026 * Draw the underline under the prefixed character
1029 * hdc [in] The handle of the DC for drawing
1030 * x [in] The x location of the line segment (logical coordinates)
1031 * y [in] The y location of where the underscore should appear
1032 * (logical coordinates)
1033 * str [in] The text of the line segment
1034 * offset [in] The offset of the underscored character within str
1035 * rect [in] Clipping rectangle (if not NULL)
1037 /* Synced with wine 1.1.32 */
1038 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
, const RECT
*rect
)
1046 GetTextExtentPointW (hdc
, str
, offset
, &size
);
1047 prefix_x
= x
+ size
.cx
;
1048 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
1049 prefix_end
= x
+ size
.cx
- 1;
1050 /* The above method may eventually be slightly wrong due to kerning etc. */
1052 /* Check for clipping */
1055 if (prefix_x
> rect
->right
|| prefix_end
< rect
->left
||
1056 y
< rect
->top
|| y
> rect
->bottom
)
1057 return; /* Completely outside */
1058 /* Partially outside */
1059 if (prefix_x
< rect
->left
) prefix_x
= rect
->left
;
1060 if (prefix_end
> rect
->right
) prefix_end
= rect
->right
;
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
);
1071 /***********************************************************************
1072 * DrawTextExW (USER32.@)
1074 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1075 * is not quite complete, especially with regard to \0. We will assume that
1076 * the returned string could have a length of up to i_count+3 and also have
1077 * a trailing \0 (which would be 4 more than a not-null-terminated string but
1078 * 3 more than a null-terminated string). If this is not so then increase
1079 * the allowance in DrawTextExA.
1081 #define MAX_BUFFER 1024
1085 * Synced with wine 1.1.32
1087 INT WINAPI
DrawTextExW( HDC hdc
, LPWSTR str
, INT i_count
,
1088 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
1091 const WCHAR
*strPtr
;
1092 WCHAR
*retstr
, *p_retstr
;
1094 WCHAR line
[MAX_BUFFER
];
1095 int len
, lh
, count
=i_count
;
1097 int lmargin
= 0, rmargin
= 0;
1098 int x
= rect
->left
, y
= rect
->top
;
1099 int width
= rect
->right
- rect
->left
;
1102 int tabwidth
/* to keep gcc happy */ = 0;
1104 ellipsis_data ellip
;
1107 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str
, count
), count
,
1108 wine_dbgstr_rect(rect
), flags
);
1110 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1111 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
1117 if (flags
& DT_SINGLELINE
)
1118 flags
&= ~DT_WORDBREAK
;
1120 GetTextMetricsW(hdc
, &tm
);
1121 if (flags
& DT_EXTERNALLEADING
)
1122 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1126 if (str
[0] && count
== 0)
1129 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1134 count
= strlenW(str
);
1137 if( flags
& DT_CALCRECT
)
1139 rect
->right
= rect
->left
;
1140 if( flags
& DT_SINGLELINE
)
1141 rect
->bottom
= rect
->top
+ lh
;
1143 rect
->bottom
= rect
->top
;
1149 if (GetGraphicsMode(hdc
) == GM_COMPATIBLE
)
1151 SIZE window_ext
, viewport_ext
;
1152 GetWindowExtEx(hdc
, &window_ext
);
1153 GetViewportExtEx(hdc
, &viewport_ext
);
1154 if ((window_ext
.cy
> 0) != (viewport_ext
.cy
> 0))
1160 lmargin
= dtp
->iLeftMargin
;
1161 rmargin
= dtp
->iRightMargin
;
1162 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
1164 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
1167 if (flags
& DT_EXPANDTABS
)
1169 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
) ? dtp
->iTabLength
: 8;
1170 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
1173 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
1175 if (flags
& DT_MODIFYSTRING
)
1177 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
1178 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
1179 if (!retstr
) return 0;
1180 memcpy (retstr
, str
, size_retstr
);
1191 len
= sizeof(line
)/sizeof(line
[0]);
1193 last_line
= !(flags
& DT_NOCLIP
) && y
- ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) < rect
->bottom
;
1195 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
1196 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
, tabwidth
, &prefix_offset
, &ellip
);
1198 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
1200 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
1202 if (flags
& DT_SINGLELINE
)
1204 if (flags
& DT_VCENTER
) y
= rect
->top
+
1205 (rect
->bottom
- rect
->top
) / 2 - size
.cy
/ 2;
1206 else if (flags
& DT_BOTTOM
) y
= rect
->bottom
- size
.cy
;
1209 if (!(flags
& DT_CALCRECT
))
1211 const WCHAR
*str
= line
;
1217 if ((flags
& DT_EXPANDTABS
))
1220 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
1222 if (len_seg
!= len
&& !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
1228 if (!ExtTextOutW( hdc
, xseg
, y
,
1229 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1230 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1231 rect
, str
, len_seg
, NULL
)) return 0;
1232 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
1234 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
, (flags
& DT_NOCLIP
) ? NULL
: rect
);
1240 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
1242 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
1243 if (prefix_offset
!= -1)
1245 if (prefix_offset
< len_seg
)
1247 /* We have just drawn an underscore; we ought to
1248 * figure out where the next one is. I am going
1249 * to leave it for now until I have a better model
1250 * for the line, which will make reprefixing easier.
1251 * This is where ellip would be used.
1256 prefix_offset
-= len_seg
;
1261 else if (size
.cx
> max_width
)
1262 max_width
= size
.cx
;
1269 dtp
->uiLengthDrawn
+= len
;
1271 while (strPtr
&& !last_line
);
1273 if (flags
& DT_CALCRECT
)
1275 rect
->right
= rect
->left
+ max_width
;
1278 rect
->right
+= lmargin
+ rmargin
;
1282 memcpy (str
, retstr
, size_retstr
);
1283 HeapFree (GetProcessHeap(), 0, retstr
);
1285 return y
- rect
->top
;
1290 GdiGetCodePage(HDC hdc
);
1292 /***********************************************************************
1293 * DrawTextExA (USER32.@)
1295 * If DT_MODIFYSTRING is specified then there must be room for up to
1296 * 4 extra characters. We take great care about just how much modified
1301 * Synced with wine 1.1.32
1303 INT WINAPI
DrawTextExA( HDC hdc
, LPSTR str
, INT count
,
1304 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
1315 if (!count
) return 0;
1316 if (!str
&& count
> 0) return 0;
1317 if( !str
|| ((count
== -1) && !(count
= strlen(str
))))
1322 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1325 GetTextMetricsA(hdc
, &tm
);
1326 if (flags
& DT_EXTERNALLEADING
)
1327 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1331 if( flags
& DT_CALCRECT
)
1333 rect
->right
= rect
->left
;
1334 if( flags
& DT_SINGLELINE
)
1335 rect
->bottom
= rect
->top
+ lh
;
1337 rect
->bottom
= rect
->top
;
1341 cp
= GdiGetCodePage( hdc
);
1342 wcount
= MultiByteToWideChar( cp
, 0, str
, count
, NULL
, 0 );
1345 if (flags
& DT_MODIFYSTRING
)
1350 wstr
= HeapAlloc(GetProcessHeap(), 0, wmax
* sizeof(WCHAR
));
1353 MultiByteToWideChar( cp
, 0, str
, count
, wstr
, wcount
);
1354 if (flags
& DT_MODIFYSTRING
)
1355 for (i
=4, p
=wstr
+wcount
; i
--; p
++) *p
=0xFFFE;
1356 /* Initialise the extra characters so that we can see which ones
1357 * change. U+FFFE is guaranteed to be not a unicode character and
1358 * so will not be generated by DrawTextEx itself.
1360 ret
= DrawTextExW( hdc
, wstr
, wcount
, rect
, flags
, dtp
);
1361 if (flags
& DT_MODIFYSTRING
)
1363 /* Unfortunately the returned string may contain multiple \0s
1364 * and so we need to measure it ourselves.
1366 for (i
=4, p
=wstr
+wcount
; i
-- && *p
!= 0xFFFE; p
++) wcount
++;
1367 WideCharToMultiByte( cp
, 0, wstr
, wcount
, str
, amax
, NULL
, NULL
);
1369 HeapFree(GetProcessHeap(), 0, wstr
);
1374 /***********************************************************************
1375 * DrawTextW (USER32.@)
1379 INT WINAPI
DrawTextW( HDC hdc
, LPCWSTR str
, INT count
, LPRECT rect
, UINT flags
)
1383 memset (&dtp
, 0, sizeof(dtp
));
1384 dtp
.cbSize
= sizeof(dtp
);
1385 if (flags
& DT_TABSTOP
)
1387 dtp
.iTabLength
= (flags
>> 8) & 0xff;
1388 flags
&= 0xffff00ff;
1390 return DrawTextExW(hdc
, (LPWSTR
)str
, count
, rect
, flags
, &dtp
);
1393 /***********************************************************************
1394 * DrawTextA (USER32.@)
1398 INT WINAPI
DrawTextA( HDC hdc
, LPCSTR str
, INT count
, LPRECT rect
, UINT flags
)
1402 memset (&dtp
, 0, sizeof(dtp
));
1403 dtp
.cbSize
= sizeof(dtp
);
1404 if (flags
& DT_TABSTOP
)
1406 dtp
.iTabLength
= (flags
>> 8) & 0xff;
1407 flags
&= 0xffff00ff;
1409 return DrawTextExA( hdc
, (LPSTR
)str
, count
, rect
, flags
, &dtp
);