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: lib/user32/windows/input.c
23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * 09-05-2001 CSH Created
28 /* INCLUDES ******************************************************************/
32 #include <wine/debug.h>
34 WINE_DEFAULT_DEBUG_CHANNEL(text
);
36 DWORD WINAPI
GdiGetCodePage(HDC hdc
);
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
);
59 for(;;); /* eliminate warning by mingw */
74 CONST INT
*lpnTabStopPositions
,
80 UINT cp
= GdiGetCodePage( hDC
); // CP_ACP
82 len
= MultiByteToWideChar(cp
, 0, lpString
, nCount
, NULL
, 0);
84 strW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
86 MultiByteToWideChar(cp
, 0, lpString
, nCount
, strW
, len
);
87 ret
= TabbedTextOutW(hDC
, X
, Y
, strW
, len
, nTabPositions
, lpnTabStopPositions
, nTabOrigin
);
88 HeapFree(GetProcessHeap(), 0, strW
);
93 /***********************************************************************
96 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
97 * Note: this doesn't work too well for text-alignment modes other
98 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
100 /* WINE synced 22-May-2006 */
101 static LONG
TEXT_TabbedTextOut( HDC hdc
, INT x
, INT y
, LPCWSTR lpstr
,
102 INT count
, INT cTabStops
, const INT
*lpTabPos
, INT nTabOrg
,
115 defWidth
= *lpTabPos
;
121 GetTextMetricsA( hdc
, &tm
);
122 defWidth
= 8 * tm
.tmAveCharWidth
;
131 /* chop the string into substrings of 0 or more <tabs>
132 * possibly followed by 1 or more normal characters */
133 for (i
= 0; i
< count
; i
++)
134 if (lpstr
[i
] != '\t') break;
135 for (j
= i
; j
< count
; j
++)
136 if (lpstr
[j
] == '\t') break;
137 /* get the extent of the normal character part */
138 GetTextExtentPointW( hdc
, lpstr
+ i
, j
- i
, &extent
);
139 /* and if there is a <tab>, calculate its position */
141 /* get x coordinate for the drawing of this string */
142 for (; cTabStops
> i
; lpTabPos
++, cTabStops
--)
144 if( nTabOrg
+ abs( *lpTabPos
) > x
) {
145 if( lpTabPos
[ i
- 1] >= 0) {
146 /* a left aligned tab */
147 x
= nTabOrg
+ lpTabPos
[ i
-1] + extent
.cx
;
152 /* if tab pos is negative then text is right-aligned
153 * to tab stop meaning that the string extends to the
154 * left, so we must subtract the width of the string */
155 if (nTabOrg
- lpTabPos
[ i
- 1] - extent
.cx
> x
)
157 x
= nTabOrg
- lpTabPos
[ i
- 1];
164 /* if we have run out of tab stops and we have a valid default tab
165 * stop width then round x up to that width */
166 if ((cTabStops
<= i
) && (defWidth
> 0)) {
167 x0
= nTabOrg
+ ((x
- nTabOrg
) / defWidth
+ i
) * defWidth
;
169 } else if ((cTabStops
<= i
) && (defWidth
< 0)) {
170 x
= nTabOrg
+ ((x
- nTabOrg
+ extent
.cx
) / -defWidth
+ i
)
181 r
.bottom
= y
+ extent
.cy
;
182 ExtTextOutW( hdc
, x0
, y
, GetBkMode(hdc
) == OPAQUE
? ETO_OPAQUE
: 0,
183 &r
, lpstr
+ i
, j
- i
, NULL
);
188 return MAKELONG(x
- start
, extent
.cy
);
203 CONST INT
*lpnTabStopPositions
,
206 return TEXT_TabbedTextOut(hDC
, X
, Y
, lpString
, nCount
, nTabPositions
, lpnTabStopPositions
, nTabOrigin
, TRUE
);
209 /* WINE synced 22-May-2006 */
215 GetTabbedTextExtentA(
220 CONST INT
*lpnTabStopPositions
)
223 UINT cp
= GdiGetCodePage( hDC
); // CP_ACP
227 len
= MultiByteToWideChar(cp
, 0, lpString
, nCount
, NULL
, 0);
229 strW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
231 MultiByteToWideChar(cp
, 0, lpString
, nCount
, strW
, len
);
232 ret
= GetTabbedTextExtentW(hDC
, strW
, len
, nTabPositions
, lpnTabStopPositions
);
233 HeapFree(GetProcessHeap(), 0, strW
);
242 GetTabbedTextExtentW(
247 CONST INT
*lpnTabStopPositions
)
249 return TEXT_TabbedTextOut(hDC
, 0, 0, lpString
, nCount
, nTabPositions
, lpnTabStopPositions
, 0, FALSE
);
252 /*********************************************************************
257 * Copyright 1993, 1994 Alexandre Julliard
258 * Copyright 2002 Bill Medland
261 * How many buffers to use
262 * While processing in DrawText there are potentially three different forms
263 * of the text that need to be held. How are they best held?
264 * 1. The original text is needed, of course, to see what to display.
265 * 2. The text that will be returned to the user if the DT_MODIFYSTRING is
267 * 3. The buffered text that is about to be displayed e.g. the current line.
268 * Typically this will exclude the ampersands used for prefixing etc.
271 * a. If the buffered text to be displayed includes the ampersands then
272 * we will need special measurement and draw functions that will ignore
273 * the ampersands (e.g. by copying to a buffer without the prefix and
274 * then using the normal forms). This may involve less space but may
275 * require more processing. e.g. since a line containing tabs may
276 * contain several underlined characters either we need to carry around
277 * a list of prefix locations or we may need to locate them several
279 * b. If we actually directly modify the "original text" as we go then we
280 * will need some special "caching" to handle the fact that when we
281 * ellipsify the text the ellipsis may modify the next line of text,
282 * which we have not yet processed. (e.g. ellipsification of a W at the
283 * end of a line will overwrite the W, the \n and the first character of
284 * the next line, and a \0 will overwrite the second. Try it!!)
286 * Option 1. Three separate storages. (To be implemented)
287 * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
288 * the edited string in some form, either as the string itself or as some
289 * sort of "edit list" to be applied just before returning.
290 * Use a buffer that holds the ellipsified current line sans ampersands
291 * and accept the need occasionally to recalculate the prefixes (if
292 * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
300 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
301 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
303 #define FORWARD_SLASH '/'
304 #define BACK_SLASH '\\'
306 static const WCHAR ELLIPSISW
[] = {'.','.','.', 0};
308 typedef struct tag_ellipsis_data
316 /*********************************************************************
317 * TEXT_Ellipsify (static)
319 * Add an ellipsis to the end of the given string whilst ensuring it fits.
321 * If the ellipsis alone doesn't fit then it will be returned anyway.
323 * See Also TEXT_PathEllipsify
326 * hdc [in] The handle to the DC that defines the font.
327 * str [in/out] The string that needs to be modified.
328 * max_str [in] The dimension of str (number of WCHAR).
329 * len_str [in/out] The number of characters in str
330 * width [in] The maximum width permitted (in logical coordinates)
331 * size [out] The dimensions of the text
332 * modstr [out] The modified form of the string, to be returned to the
333 * calling program. It is assumed that the caller has
334 * made sufficient space available so we don't need to
335 * know the size of the space. This pointer may be NULL if
336 * the modified string is not required.
337 * len_before [out] The number of characters before the ellipsis.
338 * len_ellip [out] The number of characters in the ellipsis.
340 * See for example Microsoft article Q249678.
342 * For now we will simply use three dots rather than worrying about whether
343 * the font contains an explicit ellipsis character.
345 static void TEXT_Ellipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
346 unsigned int *len_str
, int width
, SIZE
*size
,
348 int *len_before
, int *len_ellip
)
350 unsigned int len_ellipsis
;
351 unsigned int lo
, mid
, hi
;
353 len_ellipsis
= strlenW (ELLIPSISW
);
354 if (len_ellipsis
> max_len
) len_ellipsis
= max_len
;
355 if (*len_str
> max_len
- len_ellipsis
)
356 *len_str
= max_len
- len_ellipsis
;
358 /* First do a quick binary search to get an upper bound for *len_str. */
360 GetTextExtentExPointW(hdc
, str
, *len_str
, width
, NULL
, NULL
, size
) &&
363 for (lo
= 0, hi
= *len_str
; lo
< hi
; )
366 if (!GetTextExtentExPointW(hdc
, str
, mid
, width
, NULL
, NULL
, size
))
368 if (size
->cx
> width
)
375 /* Now this should take only a couple iterations at most. */
378 memcpy(str
+ *len_str
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
380 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
381 NULL
, NULL
, size
)) break;
383 if (!*len_str
|| size
->cx
<= width
) break;
387 *len_ellip
= len_ellipsis
;
388 *len_before
= *len_str
;
389 *len_str
+= len_ellipsis
;
393 memcpy (modstr
, str
, *len_str
* sizeof(WCHAR
));
394 modstr
[*len_str
] = '\0';
398 /*********************************************************************
399 * TEXT_PathEllipsify (static)
401 * Add an ellipsis to the provided string in order to make it fit within
402 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
405 * See Also TEXT_Ellipsify
408 * hdc [in] The handle to the DC that defines the font.
409 * str [in/out] The string that needs to be modified
410 * max_str [in] The dimension of str (number of WCHAR).
411 * len_str [in/out] The number of characters in str
412 * width [in] The maximum width permitted (in logical coordinates)
413 * size [out] The dimensions of the text
414 * modstr [out] The modified form of the string, to be returned to the
415 * calling program. It is assumed that the caller has
416 * made sufficient space available so we don't need to
417 * know the size of the space. This pointer may be NULL if
418 * the modified string is not required.
419 * pellip [out] The ellipsification results
421 * For now we will simply use three dots rather than worrying about whether
422 * the font contains an explicit ellipsis character.
424 * The following applies, I think to Win95. We will need to extend it for
425 * Win98 which can have both path and end ellipsis at the same time (e.g.
426 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
428 * The resulting string consists of as much as possible of the following:
429 * 1. The ellipsis itself
430 * 2. The last \ or / of the string (if any)
431 * 3. Everything after the last \ or / of the string (if any) or the whole
432 * string if there is no / or \. I believe that under Win95 this would
433 * include everything even though some might be clipped off the end whereas
434 * under Win98 that might be ellipsified too.
435 * Yet to be investigated is whether this would include wordbreaking if the
436 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
437 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
438 * broken within words).
439 * 4. All the stuff before the / or \, which is placed before the ellipsis.
441 static void TEXT_PathEllipsify (HDC hdc
, WCHAR
*str
, unsigned int max_len
,
442 unsigned int *len_str
, int width
, SIZE
*size
,
443 WCHAR
*modstr
, ellipsis_data
*pellip
)
448 WCHAR
*lastBkSlash
, *lastFwdSlash
, *lastSlash
;
450 len_ellipsis
= strlenW (ELLIPSISW
);
451 if (!max_len
) return;
452 if (len_ellipsis
>= max_len
) len_ellipsis
= max_len
- 1;
453 if (*len_str
+ len_ellipsis
>= max_len
)
454 *len_str
= max_len
- len_ellipsis
-1;
455 /* Hopefully this will never happen, otherwise it would probably lose
456 * the wrong character
458 str
[*len_str
] = '\0'; /* to simplify things */
460 lastBkSlash
= strrchrW (str
, BACK_SLASH
);
461 lastFwdSlash
= strrchrW (str
, FORWARD_SLASH
);
462 lastSlash
= lastBkSlash
> lastFwdSlash
? lastBkSlash
: lastFwdSlash
;
463 if (!lastSlash
) lastSlash
= str
;
464 len_trailing
= *len_str
- (lastSlash
- str
);
466 /* overlap-safe movement to the right */
467 memmove (lastSlash
+len_ellipsis
, lastSlash
, len_trailing
* sizeof(WCHAR
));
468 memcpy (lastSlash
, ELLIPSISW
, len_ellipsis
*sizeof(WCHAR
));
469 len_trailing
+= len_ellipsis
;
470 /* From this point on lastSlash actually points to the ellipsis in front
471 * of the last slash and len_trailing includes the ellipsis
477 if (!GetTextExtentExPointW (hdc
, str
, *len_str
+ len_ellipsis
, width
,
478 NULL
, NULL
, size
)) break;
480 if (lastSlash
== str
|| size
->cx
<= width
) break;
482 /* overlap-safe movement to the left */
483 memmove (lastSlash
-1, lastSlash
, len_trailing
* sizeof(WCHAR
));
490 pellip
->before
= lastSlash
-str
;
491 pellip
->len
= len_ellipsis
;
492 pellip
->under
= len_under
;
493 pellip
->after
= len_trailing
- len_ellipsis
;
494 *len_str
+= len_ellipsis
;
498 memcpy(modstr
, str
, *len_str
* sizeof(WCHAR
));
499 modstr
[*len_str
] = '\0';
503 /* Check the character is Chinese, Japanese, Korean and/or Thai */
504 inline BOOL
IsCJKT(WCHAR wch
)
506 if (0x0E00 <= wch
&& wch
<= 0x0E7F)
507 return TRUE
; /* Thai */
509 if (0x3000 <= wch
&& wch
<= 0x9FFF)
510 return TRUE
; /* CJK */
512 if (0xAC00 <= wch
&& wch
<= 0xD7FF)
513 return TRUE
; /* Korean */
515 if (0xFF00 <= wch
&& wch
<= 0xFFEF)
516 return TRUE
; /* CJK */
521 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
522 static const WCHAR KinsokuClassA
[] =
524 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
525 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
526 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
527 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
528 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
529 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
530 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
531 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
532 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
535 /*********************************************************************
536 * TEXT_WordBreak (static)
538 * Perform wordbreak processing on the given string
540 * Assumes that DT_WORDBREAK has been specified and not all the characters
541 * fit. Note that this function should even be called when the first character
542 * that doesn't fit is known to be a space or tab, so that it can swallow them.
544 * Note that the Windows processing has some strange properties.
545 * 1. If the text is left-justified and there is room for some of the spaces
546 * that follow the last word on the line then those that fit are included on
548 * 2. If the text is centred or right-justified and there is room for some of
549 * the spaces that follow the last word on the line then all but one of those
550 * that fit are included on the line.
551 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
552 * character of a new line it will be skipped.
555 * hdc [in] The handle to the DC that defines the font.
556 * str [in/out] The string that needs to be broken.
557 * max_str [in] The dimension of str (number of WCHAR).
558 * len_str [in/out] The number of characters in str
559 * width [in] The maximum width permitted
560 * format [in] The format flags in effect
561 * chars_fit [in] The maximum number of characters of str that are already
562 * known to fit; chars_fit+1 is known not to fit.
563 * chars_used [out] The number of characters of str that have been "used" and
564 * do not need to be included in later text. For example this will
565 * include any spaces that have been discarded from the start of
567 * size [out] The size of the returned text in logical coordinates
569 * Pedantic assumption - Assumes that the text length is monotonically
570 * increasing with number of characters (i.e. no weird kernings)
574 * Work back from the last character that did fit to either a space or the last
575 * character of a word, whichever is met first.
576 * If there was one or the first character didn't fit then
577 * If the text is centred or right justified and that one character was a
578 * space then break the line before that character
579 * Otherwise break the line after that character
580 * and if the next character is a space then discard it.
581 * Suppose there was none (and the first character did fit).
582 * If Break Within Word is permitted
583 * break the word after the last character that fits (there must be
584 * at least one; none is caught earlier).
586 * discard any trailing space.
587 * include the whole word; it may be ellipsified later
589 * Break Within Word is permitted under a set of circumstances that are not
590 * totally clear yet. Currently our best guess is:
591 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
592 * DT_PATH_ELLIPSIS is
595 static void TEXT_WordBreak (HDC hdc
, WCHAR
*str
, unsigned int max_str
,
596 unsigned int *len_str
,
597 int width
, int format
, unsigned int chars_fit
,
598 unsigned int *chars_used
, SIZE
*size
)
602 assert (format
& DT_WORDBREAK
);
603 assert (chars_fit
< *len_str
);
605 /* Work back from the last character that did fit to either a space or the
606 * last character of a word, whichever is met first.
608 p
= str
+ chars_fit
; /* The character that doesn't fit */
612 else if (*p
== SPACE
) /* chars_fit < *len_str so this is valid */
613 p
--; /* the word just fitted */
616 while (p
> str
&& *(--p
) != SPACE
&& (!IsCJKT(p
[1]) ||
617 p
[1] == L
'\0' || wcschr(KinsokuClassA
, p
[1]) != NULL
))
619 word_fits
= (p
!= str
|| *p
== SPACE
|| IsCJKT(p
[1]));
621 /* If there was one. */
625 /* break the line before/after that character */
626 if (!(format
& (DT_RIGHT
| DT_CENTER
)) || *p
!= SPACE
)
628 next_is_space
= (unsigned int) (p
- str
) < *len_str
&& *p
== SPACE
;
630 /* and if the next character is a space then discard it. */
631 *chars_used
= *len_str
;
635 /* Suppose there was none. */
638 if ((format
& (DT_EDITCONTROL
| DT_WORD_ELLIPSIS
| DT_PATH_ELLIPSIS
)) ==
641 /* break the word after the last character that fits (there must be
645 *len_str
= chars_fit
;
646 *chars_used
= chars_fit
;
648 /* FIXME - possible error. Since the next character is now removed
649 * this could make the text longer so that it no longer fits, and
650 * so we need a loop to test and shrink.
656 /* discard any trailing space. */
657 const WCHAR
*e
= str
+ *len_str
;
659 while (p
< e
&& *p
!= SPACE
)
661 *chars_used
= p
- str
;
662 if (p
< e
) /* i.e. loop failed because *p == SPACE */
665 /* include the whole word; it may be ellipsified later */
667 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
668 * so that it will be too long
672 /* Remeasure the string */
673 GetTextExtentExPointW (hdc
, str
, *len_str
, 0, NULL
, NULL
, size
);
676 /*********************************************************************
679 * Skip over the given number of characters, bearing in mind prefix
680 * substitution and the fact that a character may take more than one
681 * WCHAR (Unicode surrogates are two words long) (and there may have been
685 * new_count [out] The updated count
686 * new_str [out] The updated pointer
687 * start_count [in] The count of remaining characters corresponding to the
688 * start of the string
689 * start_str [in] The starting point of the string
690 * max [in] The number of characters actually in this segment of the
691 * string (the & counts)
692 * n [in] The number of characters to skip (if prefix then
694 * prefix [in] Apply prefix substitution
700 * There must be at least n characters in the string
701 * We need max because the "line" may have ended with a & followed by a tab
702 * or newline etc. which we don't want to swallow
705 static void TEXT_SkipChars (int *new_count
, const WCHAR
**new_str
,
706 int start_count
, const WCHAR
*start_str
,
707 int max
, int n
, int prefix
)
709 /* This is specific to wide characters, MSDN doesn't say anything much
710 * about Unicode surrogates yet and it isn't clear if _wcsinc will
711 * correctly handle them so we'll just do this the easy way for now
716 const WCHAR
*str_on_entry
= start_str
;
721 if ((*start_str
== PREFIX
|| *start_str
== ALPHA_PREFIX
) && max
--)
725 start_count
-= (start_str
- str_on_entry
);
732 *new_str
= start_str
;
733 *new_count
= start_count
;
736 /*********************************************************************
739 * Reanalyse the text to find the prefixed character. This is called when
740 * wordbreaking or ellipsification has shortened the string such that the
741 * previously noted prefixed character is no longer visible.
744 * str [in] The original string segment (including all characters)
745 * ns [in] The number of characters in str (including prefixes)
746 * pe [in] The ellipsification data
749 * The prefix offset within the new string segment (the one that contains the
750 * ellipses and does not contain the prefix characters) (-1 if none)
753 static int TEXT_Reprefix (const WCHAR
*str
, unsigned int ns
,
754 const ellipsis_data
*pe
)
758 unsigned int n
= pe
->before
+ pe
->under
+ pe
->after
;
760 for (i
= 0; i
< n
; i
++, str
++)
762 if (i
== (unsigned int) pe
->before
)
764 /* Reached the path ellipsis; jump over it */
765 if (ns
< (unsigned int) pe
->under
) break;
769 if (!pe
->after
) break; /* Nothing after the path ellipsis */
773 if (*str
++ == PREFIX
|| *str
== ALPHA_PREFIX
)
778 result
= (i
< (unsigned int) pe
->before
|| pe
->under
== 0) ? i
: i
- pe
->under
+ pe
->len
;
779 /* pe->len may be non-zero while pe_under is zero */
786 /*********************************************************************
787 * Returns true if and only if the remainder of the line is a single
788 * newline representation or nothing
791 static int remainder_is_none_or_newline (int num_chars
, const WCHAR
*str
)
793 if (!num_chars
) return TRUE
;
794 if (*str
!= LF
&& *str
!= CR
) return FALSE
;
795 if (!--num_chars
) return TRUE
;
796 if (*str
== *(str
+1)) return FALSE
;
798 if (*str
!= CR
&& *str
!= LF
) return FALSE
;
799 if (--num_chars
) return FALSE
;
803 /*********************************************************************
804 * Return next line of text from a string.
806 * hdc - handle to DC.
807 * str - string to parse into lines.
808 * count - length of str.
809 * dest - destination in which to return line.
810 * len - dest buffer size in chars on input, copied length into dest on output.
811 * width - maximum width of line in pixels.
812 * format - format type passed to DrawText.
813 * retsize - returned size of the line in pixels.
814 * last_line - TRUE if is the last line that will be processed
815 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
816 * the return string is built.
817 * tabwidth - The width of a tab in logical coordinates
818 * pprefix_offset - Here is where we return the offset within dest of the first
819 * prefixed (underlined) character. -1 is returned if there
820 * are none. Note that there may be more; the calling code
821 * will need to use TEXT_Reprefix to find any later ones.
822 * pellip - Here is where we return the information about any ellipsification
823 * that was carried out. Note that if tabs are being expanded then
824 * this data will correspond to the last text segment actually
825 * returned in dest; by definition there would not have been any
826 * ellipsification in earlier text segments of the line.
828 * Returns pointer to next char in str after end of the line
829 * or NULL if end of str reached.
831 static const WCHAR
*TEXT_NextLineW( HDC hdc
, const WCHAR
*str
, int *count
,
832 WCHAR
*dest
, int *len
, int width
, DWORD format
,
833 SIZE
*retsize
, int last_line
, WCHAR
**p_retstr
,
834 int tabwidth
, int *pprefix_offset
,
835 ellipsis_data
*pellip
)
841 int seg_i
, seg_count
, seg_j
;
846 unsigned int j_in_seg
;
848 *pprefix_offset
= -1;
850 /* For each text segment in the line */
856 /* Skip any leading tabs */
858 if (str
[i
] == TAB
&& (format
& DT_EXPANDTABS
))
860 plen
= ((plen
/tabwidth
)+1)*tabwidth
;
861 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
862 while (*count
&& str
[i
] == TAB
)
865 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
870 /* Now copy as far as the next tab or cr/lf or eos */
877 (str
[i
] != TAB
|| !(format
& DT_EXPANDTABS
)) &&
878 ((str
[i
] != CR
&& str
[i
] != LF
) || (format
& DT_SINGLELINE
)))
880 if ((format
& DT_NOPREFIX
) || *count
<= 1)
882 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
886 if (str
[i
] == PREFIX
|| str
[i
] == ALPHA_PREFIX
) {
887 (*count
)--, i
++; /* Throw away the prefix itself */
888 if (str
[i
] == PREFIX
)
890 /* Swallow it before we see it again */
891 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
893 else if (*pprefix_offset
== -1 || *pprefix_offset
>= seg_j
)
897 /* else the previous prefix was in an earlier segment of the
898 * line; we will leave it to the drawing code to catch this
902 else if (str
[i
] == KANA_PREFIX
)
904 /* Throw away katakana access keys */
905 (*count
)--, i
++; /* skip the prefix */
906 (*count
)--, i
++; /* skip the letter */
910 (*count
)--; if (j
< maxl
) dest
[j
++] = str
[i
++]; else i
++;
915 /* Measure the whole text segment and possibly WordBreak and
919 j_in_seg
= j
- seg_j
;
920 max_seg_width
= width
- plen
;
921 GetTextExtentExPointW (hdc
, dest
+ seg_j
, j_in_seg
, max_seg_width
, &num_fit
, NULL
, &size
);
923 /* The Microsoft handling of various combinations of formats is weird.
924 * The following may very easily be incorrect if several formats are
925 * combined, and may differ between versions (to say nothing of the
926 * several bugs in the Microsoft versions).
929 line_fits
= (num_fit
>= j_in_seg
);
930 if (!line_fits
&& (format
& DT_WORDBREAK
))
933 unsigned int chars_used
;
934 TEXT_WordBreak (hdc
, dest
+seg_j
, maxl
-seg_j
, &j_in_seg
,
935 max_seg_width
, format
, num_fit
, &chars_used
, &size
);
936 line_fits
= (size
.cx
<= max_seg_width
);
937 /* and correct the counts */
938 TEXT_SkipChars (count
, &s
, seg_count
, str
+seg_i
, i
-seg_i
,
939 chars_used
, !(format
& DT_NOPREFIX
));
943 pellip
->before
= j_in_seg
;
948 if (!line_fits
&& (format
& DT_PATH_ELLIPSIS
))
950 TEXT_PathEllipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
951 max_seg_width
, &size
, *p_retstr
, pellip
);
952 line_fits
= (size
.cx
<= max_seg_width
);
955 /* NB we may end up ellipsifying a word-broken or path_ellipsified
957 if ((!line_fits
&& (format
& DT_WORD_ELLIPSIS
)) ||
958 ((format
& DT_END_ELLIPSIS
) &&
959 ((last_line
&& *count
) ||
960 (remainder_is_none_or_newline (*count
, &str
[i
]) && !line_fits
))))
962 int before
, len_ellipsis
;
963 TEXT_Ellipsify (hdc
, dest
+ seg_j
, maxl
-seg_j
, &j_in_seg
,
964 max_seg_width
, &size
, *p_retstr
, &before
, &len_ellipsis
);
965 if (before
> pellip
->before
)
967 /* We must have done a path ellipsis too */
968 pellip
->after
= before
- pellip
->before
- pellip
->len
;
969 /* Leave the len as the length of the first ellipsis */
973 /* If we are here after a path ellipsification it must be
974 * because even the ellipsis itself didn't fit.
976 assert (pellip
->under
== 0 && pellip
->after
== 0);
977 pellip
->before
= before
;
978 pellip
->len
= len_ellipsis
;
979 /* pellip->after remains as zero as does
983 line_fits
= (size
.cx
<= max_seg_width
);
986 /* As an optimisation if we have ellipsified and we are expanding
987 * tabs and we haven't reached the end of the line we can skip to it
988 * now rather than going around the loop again.
990 if ((format
& DT_EXPANDTABS
) && ellipsified
)
992 if (format
& DT_SINGLELINE
)
996 while ((*count
) && str
[i
] != CR
&& str
[i
] != LF
)
1003 j
= seg_j
+ j_in_seg
;
1004 if (*pprefix_offset
>= seg_j
+ pellip
->before
)
1006 *pprefix_offset
= TEXT_Reprefix (str
+ seg_i
, i
- seg_i
, pellip
);
1007 if (*pprefix_offset
!= -1)
1008 *pprefix_offset
+= seg_j
;
1012 if (size
.cy
> retsize
->cy
)
1013 retsize
->cy
= size
.cy
;
1019 else if (str
[i
] == CR
|| str
[i
] == LF
)
1022 if (*count
&& (str
[i
] == CR
|| str
[i
] == LF
) && str
[i
] != str
[i
-1])
1028 /* else it was a Tab and we go around again */
1040 /***********************************************************************
1041 * TEXT_DrawUnderscore
1043 * Draw the underline under the prefixed character
1046 * hdc [in] The handle of the DC for drawing
1047 * x [in] The x location of the line segment (logical coordinates)
1048 * y [in] The y location of where the underscore should appear
1049 * (logical coordinates)
1050 * str [in] The text of the line segment
1051 * offset [in] The offset of the underscored character within str
1052 * rect [in] Clipping rectangle (if not NULL)
1054 /* Synced with wine 1.1.32 */
1055 static void TEXT_DrawUnderscore (HDC hdc
, int x
, int y
, const WCHAR
*str
, int offset
, const RECT
*rect
)
1063 GetTextExtentPointW (hdc
, str
, offset
, &size
);
1064 prefix_x
= x
+ size
.cx
;
1065 GetTextExtentPointW (hdc
, str
, offset
+1, &size
);
1066 prefix_end
= x
+ size
.cx
- 1;
1067 /* The above method may eventually be slightly wrong due to kerning etc. */
1069 /* Check for clipping */
1072 if (prefix_x
> rect
->right
|| prefix_end
< rect
->left
||
1073 y
< rect
->top
|| y
> rect
->bottom
)
1074 return; /* Completely outside */
1075 /* Partially outside */
1076 if (prefix_x
< rect
->left
) prefix_x
= rect
->left
;
1077 if (prefix_end
> rect
->right
) prefix_end
= rect
->right
;
1080 hpen
= CreatePen (PS_SOLID
, 1, GetTextColor (hdc
));
1081 oldPen
= SelectObject (hdc
, hpen
);
1082 MoveToEx (hdc
, prefix_x
, y
, NULL
);
1083 LineTo (hdc
, prefix_end
, y
);
1084 SelectObject (hdc
, oldPen
);
1085 DeleteObject (hpen
);
1088 /***********************************************************************
1089 * DrawTextExW (USER32.@)
1091 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1092 * is not quite complete, especially with regard to \0. We will assume that
1093 * the returned string could have a length of up to i_count+3 and also have
1094 * a trailing \0 (which would be 4 more than a not-null-terminated string but
1095 * 3 more than a null-terminated string). If this is not so then increase
1096 * the allowance in DrawTextExA.
1098 #define MAX_BUFFER 1024
1102 * Synced with Wine Staging 1.7.37
1104 INT WINAPI
DrawTextExW( HDC hdc
, LPWSTR str
, INT i_count
,
1105 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
1108 const WCHAR
*strPtr
;
1109 WCHAR
*retstr
, *p_retstr
;
1111 WCHAR line
[MAX_BUFFER
];
1112 int len
, lh
, count
=i_count
;
1114 int lmargin
= 0, rmargin
= 0;
1115 int x
= rect
->left
, y
= rect
->top
;
1116 int width
= rect
->right
- rect
->left
;
1119 int tabwidth
/* to keep gcc happy */ = 0;
1121 ellipsis_data ellip
;
1122 BOOL invert_y
=FALSE
;
1124 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str
, count
), count
,
1125 wine_dbgstr_rect(rect
), flags
);
1127 if (dtp
) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1128 dtp
->iTabLength
, dtp
->iLeftMargin
, dtp
->iRightMargin
);
1134 if (flags
& DT_SINGLELINE
)
1135 flags
&= ~DT_WORDBREAK
;
1137 GetTextMetricsW(hdc
, &tm
);
1138 if (flags
& DT_EXTERNALLEADING
)
1139 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1143 if (str
[0] && count
== 0)
1146 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
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 count
= strlenW(str
);
1163 if( flags
& DT_CALCRECT
)
1165 rect
->right
= rect
->left
;
1166 if( flags
& DT_SINGLELINE
)
1167 rect
->bottom
= rect
->top
+ (invert_y
? -lh
: lh
);
1169 rect
->bottom
= rect
->top
;
1177 lmargin
= dtp
->iLeftMargin
;
1178 rmargin
= dtp
->iRightMargin
;
1179 if (!(flags
& (DT_CENTER
| DT_RIGHT
)))
1181 dtp
->uiLengthDrawn
= 0; /* This param RECEIVES number of chars processed */
1184 if (flags
& DT_EXPANDTABS
)
1186 int tabstop
= ((flags
& DT_TABSTOP
) && dtp
) ? dtp
->iTabLength
: 8;
1187 tabwidth
= tm
.tmAveCharWidth
* tabstop
;
1190 if (flags
& DT_CALCRECT
) flags
|= DT_NOCLIP
;
1192 if (flags
& DT_MODIFYSTRING
)
1194 size_retstr
= (count
+ 4) * sizeof (WCHAR
);
1195 retstr
= HeapAlloc(GetProcessHeap(), 0, size_retstr
);
1196 if (!retstr
) return 0;
1197 memcpy (retstr
, str
, size_retstr
);
1208 len
= sizeof(line
)/sizeof(line
[0]);
1210 last_line
= !(flags
& DT_NOCLIP
) && y
- ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) < rect
->bottom
;
1212 last_line
= !(flags
& DT_NOCLIP
) && y
+ ((flags
& DT_EDITCONTROL
) ? 2*lh
-1 : lh
) > rect
->bottom
;
1213 strPtr
= TEXT_NextLineW(hdc
, strPtr
, &count
, line
, &len
, width
, flags
, &size
, last_line
, &p_retstr
, tabwidth
, &prefix_offset
, &ellip
);
1215 if (flags
& DT_CENTER
) x
= (rect
->left
+ rect
->right
-
1217 else if (flags
& DT_RIGHT
) x
= rect
->right
- size
.cx
;
1219 if (flags
& DT_SINGLELINE
)
1221 if (flags
& DT_VCENTER
)
1222 y
= rect
->top
+ (rect
->bottom
- rect
->top
) / 2 + (invert_y
? (size
.cy
/ 2) : (-size
.cy
/ 2));
1223 else if (flags
& DT_BOTTOM
)
1224 y
= rect
->bottom
+ (invert_y
? 0 : -size
.cy
);
1227 if (!(flags
& DT_CALCRECT
))
1229 const WCHAR
*str
= line
;
1235 if ((flags
& DT_EXPANDTABS
))
1238 p
= str
; while (p
< str
+len
&& *p
!= TAB
) p
++;
1240 if (len_seg
!= len
&& !GetTextExtentPointW(hdc
, str
, len_seg
, &size
))
1242 HeapFree (GetProcessHeap(), 0, retstr
);
1249 if (!ExtTextOutW( hdc
, xseg
, y
,
1250 ((flags
& DT_NOCLIP
) ? 0 : ETO_CLIPPED
) |
1251 ((flags
& DT_RTLREADING
) ? ETO_RTLREADING
: 0),
1252 rect
, str
, len_seg
, NULL
))
1254 HeapFree (GetProcessHeap(), 0, retstr
);
1257 if (prefix_offset
!= -1 && prefix_offset
< len_seg
)
1259 TEXT_DrawUnderscore (hdc
, xseg
, y
+ tm
.tmAscent
+ 1, str
, prefix_offset
, (flags
& DT_NOCLIP
) ? NULL
: rect
);
1265 assert ((flags
& DT_EXPANDTABS
) && *str
== TAB
);
1267 xseg
+= ((size
.cx
/tabwidth
)+1)*tabwidth
;
1268 if (prefix_offset
!= -1)
1270 if (prefix_offset
< len_seg
)
1272 /* We have just drawn an underscore; we ought to
1273 * figure out where the next one is. I am going
1274 * to leave it for now until I have a better model
1275 * for the line, which will make reprefixing easier.
1276 * This is where ellip would be used.
1281 prefix_offset
-= len_seg
;
1286 else if (size
.cx
> max_width
)
1287 max_width
= size
.cx
;
1289 y
+= invert_y
? -lh
: lh
;
1291 dtp
->uiLengthDrawn
+= len
;
1293 while (strPtr
&& !last_line
);
1295 if (flags
& DT_CALCRECT
)
1297 rect
->right
= rect
->left
+ max_width
;
1300 rect
->right
+= lmargin
+ rmargin
;
1304 memcpy (str
, retstr
, size_retstr
);
1305 HeapFree (GetProcessHeap(), 0, retstr
);
1307 return y
- rect
->top
;
1310 /***********************************************************************
1311 * DrawTextExA (USER32.@)
1313 * If DT_MODIFYSTRING is specified then there must be room for up to
1314 * 4 extra characters. We take great care about just how much modified
1319 * Synced with Wine Staging 1.7.37
1321 INT WINAPI
DrawTextExA( HDC hdc
, LPSTR str
, INT count
,
1322 LPRECT rect
, UINT flags
, LPDRAWTEXTPARAMS dtp
)
1333 if (!count
) return 0;
1334 if (!str
&& count
> 0) return 0;
1335 if( !str
|| ((count
== -1) && !(count
= strlen(str
))))
1340 if (dtp
&& dtp
->cbSize
!= sizeof(DRAWTEXTPARAMS
))
1343 GetTextMetricsA(hdc
, &tm
);
1344 if (flags
& DT_EXTERNALLEADING
)
1345 lh
= tm
.tmHeight
+ tm
.tmExternalLeading
;
1349 if( flags
& DT_CALCRECT
)
1351 rect
->right
= rect
->left
;
1352 if( flags
& DT_SINGLELINE
)
1353 rect
->bottom
= rect
->top
+ lh
;
1355 rect
->bottom
= rect
->top
;
1359 cp
= GdiGetCodePage( hdc
);
1360 wcount
= MultiByteToWideChar( cp
, 0, str
, count
, NULL
, 0 );
1363 if (flags
& DT_MODIFYSTRING
)
1368 wstr
= HeapAlloc(GetProcessHeap(), 0, wmax
* sizeof(WCHAR
));
1371 MultiByteToWideChar( cp
, 0, str
, count
, wstr
, wcount
);
1372 if (flags
& DT_MODIFYSTRING
)
1373 for (i
=4, p
=wstr
+wcount
; i
--; p
++) *p
=0xFFFE;
1374 /* Initialise the extra characters so that we can see which ones
1375 * change. U+FFFE is guaranteed to be not a unicode character and
1376 * so will not be generated by DrawTextEx itself.
1378 ret
= DrawTextExW( hdc
, wstr
, wcount
, rect
, flags
, dtp
);
1379 if (flags
& DT_MODIFYSTRING
)
1381 /* Unfortunately the returned string may contain multiple \0s
1382 * and so we need to measure it ourselves.
1384 for (i
=4, p
=wstr
+wcount
; i
-- && *p
!= 0xFFFE; p
++) wcount
++;
1385 WideCharToMultiByte( cp
, 0, wstr
, wcount
, str
, amax
, NULL
, NULL
);
1387 HeapFree(GetProcessHeap(), 0, wstr
);
1392 /***********************************************************************
1393 * DrawTextW (USER32.@)
1396 * Synced with Wine Staging 1.7.37
1398 INT WINAPI
DrawTextW( HDC hdc
, LPCWSTR 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 DrawTextExW(hdc
, (LPWSTR
)str
, count
, rect
, flags
, &dtp
);
1412 /***********************************************************************
1413 * DrawTextA (USER32.@)
1416 * Synced with Wine Staging 1.7.37
1418 INT WINAPI
DrawTextA( HDC hdc
, LPCSTR str
, INT count
, LPRECT rect
, UINT flags
)
1422 memset (&dtp
, 0, sizeof(dtp
));
1423 dtp
.cbSize
= sizeof(dtp
);
1424 if (flags
& DT_TABSTOP
)
1426 dtp
.iTabLength
= (flags
>> 8) & 0xff;
1427 flags
&= 0xffff00ff;
1429 return DrawTextExA( hdc
, (LPSTR
)str
, count
, rect
, flags
, &dtp
);