Revert tree-restructure attempt: r66583, r66582, r66581, r66578, sauf ntdll changes...
[reactos.git] / reactos / win32ss / user / user32 / windows / font.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /*
20 * PROJECT: ReactOS user32.dll
21 * FILE: lib/user32/windows/input.c
22 * PURPOSE: Input
23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
24 * UPDATE HISTORY:
25 * 09-05-2001 CSH Created
26 */
27
28 /* INCLUDES ******************************************************************/
29
30 #include <user32.h>
31
32 #include <wine/debug.h>
33
34 WINE_DEFAULT_DEBUG_CHANNEL(text);
35
36 DWORD WINAPI GdiGetCodePage(HDC hdc);
37
38
39 /* FUNCTIONS *****************************************************************/
40
41 #ifndef NDEBUG
42
43 #ifdef assert
44 #undef assert
45 #endif
46
47 #define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__))
48
49 #endif
50
51 void _font_assert(const char *msg, const char *file, int line)
52 {
53 /* Assertion failed at foo.c line 45: x<y */
54 DbgPrint("Assertion failed at %s line %d: %s\n", file, line, msg);
55 ExitProcess(3);
56 for(;;); /* eliminate warning by mingw */
57 }
58
59 /*
60 * @implemented
61 */
62 LONG
63 WINAPI
64 TabbedTextOutA(
65 HDC hDC,
66 int X,
67 int Y,
68 LPCSTR lpString,
69 int nCount,
70 int nTabPositions,
71 CONST INT *lpnTabStopPositions,
72 int nTabOrigin)
73 {
74 LONG ret;
75 DWORD len;
76 LPWSTR strW;
77 UINT cp = GdiGetCodePage( hDC ); // CP_ACP
78
79 len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
80 if (!len) return 0;
81 strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
82 if (!strW) return 0;
83 MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
84 ret = TabbedTextOutW(hDC, X, Y, strW, len, nTabPositions, lpnTabStopPositions, nTabOrigin);
85 HeapFree(GetProcessHeap(), 0, strW);
86 return ret;
87 }
88
89
90 /***********************************************************************
91 * TEXT_TabbedTextOut
92 *
93 * Helper function for TabbedTextOut() and GetTabbedTextExtent().
94 * Note: this doesn't work too well for text-alignment modes other
95 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
96 */
97 /* WINE synced 22-May-2006 */
98 static LONG TEXT_TabbedTextOut( HDC hdc, INT x, INT y, LPCWSTR lpstr,
99 INT count, INT cTabStops, const INT *lpTabPos, INT nTabOrg,
100 BOOL fDisplayText )
101 {
102 INT defWidth;
103 SIZE extent;
104 int i, j;
105 int start = x;
106
107 if (!lpTabPos)
108 cTabStops=0;
109
110 if (cTabStops == 1)
111 {
112 defWidth = *lpTabPos;
113 cTabStops = 0;
114 }
115 else
116 {
117 TEXTMETRICA tm;
118 GetTextMetricsA( hdc, &tm );
119 defWidth = 8 * tm.tmAveCharWidth;
120 }
121
122 while (count > 0)
123 {
124 RECT r;
125 INT x0;
126 x0 = x;
127 r.left = x0;
128 /* chop the string into substrings of 0 or more <tabs>
129 * possibly followed by 1 or more normal characters */
130 for (i = 0; i < count; i++)
131 if (lpstr[i] != '\t') break;
132 for (j = i; j < count; j++)
133 if (lpstr[j] == '\t') break;
134 /* get the extent of the normal character part */
135 GetTextExtentPointW( hdc, lpstr + i, j - i , &extent );
136 /* and if there is a <tab>, calculate its position */
137 if( i) {
138 /* get x coordinate for the drawing of this string */
139 for (; cTabStops > i; lpTabPos++, cTabStops--)
140 {
141 if( nTabOrg + abs( *lpTabPos) > x) {
142 if( lpTabPos[ i - 1] >= 0) {
143 /* a left aligned tab */
144 x = nTabOrg + lpTabPos[ i-1] + extent.cx;
145 break;
146 }
147 else
148 {
149 /* if tab pos is negative then text is right-aligned
150 * to tab stop meaning that the string extends to the
151 * left, so we must subtract the width of the string */
152 if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x)
153 {
154 x = nTabOrg - lpTabPos[ i - 1];
155 x0 = x - extent.cx;
156 break;
157 }
158 }
159 }
160 }
161 /* if we have run out of tab stops and we have a valid default tab
162 * stop width then round x up to that width */
163 if ((cTabStops <= i) && (defWidth > 0)) {
164 x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth;
165 x = x0 + extent.cx;
166 } else if ((cTabStops <= i) && (defWidth < 0)) {
167 x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i)
168 * -defWidth;
169 x0 = x - extent.cx;
170 }
171 } else
172 x += extent.cx;
173
174 if (fDisplayText)
175 {
176 r.top = y;
177 r.right = x;
178 r.bottom = y + extent.cy;
179 ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
180 &r, lpstr + i, j - i, NULL );
181 }
182 count -= j;
183 lpstr += j;
184 }
185 return MAKELONG(x - start, extent.cy);
186 }
187
188 /*
189 * @implemented
190 */
191 LONG
192 WINAPI
193 TabbedTextOutW(
194 HDC hDC,
195 int X,
196 int Y,
197 LPCWSTR lpString,
198 int nCount,
199 int nTabPositions,
200 CONST INT *lpnTabStopPositions,
201 int nTabOrigin)
202 {
203 return TEXT_TabbedTextOut(hDC, X, Y, lpString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin, TRUE);
204 }
205
206 /* WINE synced 22-May-2006 */
207 /*
208 * @implemented
209 */
210 DWORD
211 WINAPI
212 GetTabbedTextExtentA(
213 HDC hDC,
214 LPCSTR lpString,
215 int nCount,
216 int nTabPositions,
217 CONST INT *lpnTabStopPositions)
218 {
219 LONG ret;
220 UINT cp = GdiGetCodePage( hDC ); // CP_ACP
221 DWORD len;
222 LPWSTR strW;
223
224 len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
225 if (!len) return 0;
226 strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
227 if (!strW) return 0;
228 MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
229 ret = GetTabbedTextExtentW(hDC, strW, len, nTabPositions, lpnTabStopPositions);
230 HeapFree(GetProcessHeap(), 0, strW);
231 return ret;
232 }
233
234 /*
235 * @implemented
236 */
237 DWORD
238 WINAPI
239 GetTabbedTextExtentW(
240 HDC hDC,
241 LPCWSTR lpString,
242 int nCount,
243 int nTabPositions,
244 CONST INT *lpnTabStopPositions)
245 {
246 return TEXT_TabbedTextOut(hDC, 0, 0, lpString, nCount, nTabPositions, lpnTabStopPositions, 0, FALSE);
247 }
248
249 /*********************************************************************
250 *
251 * DrawText functions
252 *
253 * Copied from Wine.
254 * Copyright 1993, 1994 Alexandre Julliard
255 * Copyright 2002 Bill Medland
256 *
257 * Design issues
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
263 * in effect.
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.
266 *
267 * Complications.
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
275 * times.
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!!)
282 *
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)
290 */
291
292 #define TAB 9
293 #define LF 10
294 #define CR 13
295 #define SPACE 32
296 #define PREFIX 38
297 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
298 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
299
300 #define FORWARD_SLASH '/'
301 #define BACK_SLASH '\\'
302
303 static const WCHAR ELLIPSISW[] = {'.','.','.', 0};
304
305 typedef struct tag_ellipsis_data
306 {
307 int before;
308 int len;
309 int under;
310 int after;
311 } ellipsis_data;
312
313 /*********************************************************************
314 * TEXT_Ellipsify (static)
315 *
316 * Add an ellipsis to the end of the given string whilst ensuring it fits.
317 *
318 * If the ellipsis alone doesn't fit then it will be returned anyway.
319 *
320 * See Also TEXT_PathEllipsify
321 *
322 * Arguments
323 * hdc [in] The handle to the DC that defines the font.
324 * str [in/out] The string that needs to be modified.
325 * max_str [in] The dimension of str (number of WCHAR).
326 * len_str [in/out] The number of characters in str
327 * width [in] The maximum width permitted (in logical coordinates)
328 * size [out] The dimensions of the text
329 * modstr [out] The modified form of the string, to be returned to the
330 * calling program. It is assumed that the caller has
331 * made sufficient space available so we don't need to
332 * know the size of the space. This pointer may be NULL if
333 * the modified string is not required.
334 * len_before [out] The number of characters before the ellipsis.
335 * len_ellip [out] The number of characters in the ellipsis.
336 *
337 * See for example Microsoft article Q249678.
338 *
339 * For now we will simply use three dots rather than worrying about whether
340 * the font contains an explicit ellipsis character.
341 */
342 static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len,
343 unsigned int *len_str, int width, SIZE *size,
344 WCHAR *modstr,
345 int *len_before, int *len_ellip)
346 {
347 unsigned int len_ellipsis;
348 unsigned int lo, mid, hi;
349
350 len_ellipsis = strlenW (ELLIPSISW);
351 if (len_ellipsis > max_len) len_ellipsis = max_len;
352 if (*len_str > max_len - len_ellipsis)
353 *len_str = max_len - len_ellipsis;
354
355 /* First do a quick binary search to get an upper bound for *len_str. */
356 if (*len_str > 0 &&
357 GetTextExtentExPointW(hdc, str, *len_str, width, NULL, NULL, size) &&
358 size->cx > width)
359 {
360 for (lo = 0, hi = *len_str; lo < hi; )
361 {
362 mid = (lo + hi) / 2;
363 if (!GetTextExtentExPointW(hdc, str, mid, width, NULL, NULL, size))
364 break;
365 if (size->cx > width)
366 hi = mid;
367 else
368 lo = mid + 1;
369 }
370 *len_str = hi;
371 }
372 /* Now this should take only a couple iterations at most. */
373 for ( ; ; )
374 {
375 memcpy(str + *len_str, ELLIPSISW, len_ellipsis*sizeof(WCHAR));
376
377 if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
378 NULL, NULL, size)) break;
379
380 if (!*len_str || size->cx <= width) break;
381
382 (*len_str)--;
383 }
384 *len_ellip = len_ellipsis;
385 *len_before = *len_str;
386 *len_str += len_ellipsis;
387
388 if (modstr)
389 {
390 memcpy (modstr, str, *len_str * sizeof(WCHAR));
391 modstr[*len_str] = '\0';
392 }
393 }
394
395 /*********************************************************************
396 * TEXT_PathEllipsify (static)
397 *
398 * Add an ellipsis to the provided string in order to make it fit within
399 * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS
400 * flag.
401 *
402 * See Also TEXT_Ellipsify
403 *
404 * Arguments
405 * hdc [in] The handle to the DC that defines the font.
406 * str [in/out] The string that needs to be modified
407 * max_str [in] The dimension of str (number of WCHAR).
408 * len_str [in/out] The number of characters in str
409 * width [in] The maximum width permitted (in logical coordinates)
410 * size [out] The dimensions of the text
411 * modstr [out] The modified form of the string, to be returned to the
412 * calling program. It is assumed that the caller has
413 * made sufficient space available so we don't need to
414 * know the size of the space. This pointer may be NULL if
415 * the modified string is not required.
416 * pellip [out] The ellipsification results
417 *
418 * For now we will simply use three dots rather than worrying about whether
419 * the font contains an explicit ellipsis character.
420 *
421 * The following applies, I think to Win95. We will need to extend it for
422 * Win98 which can have both path and end ellipsis at the same time (e.g.
423 * C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
424 *
425 * The resulting string consists of as much as possible of the following:
426 * 1. The ellipsis itself
427 * 2. The last \ or / of the string (if any)
428 * 3. Everything after the last \ or / of the string (if any) or the whole
429 * string if there is no / or \. I believe that under Win95 this would
430 * include everything even though some might be clipped off the end whereas
431 * under Win98 that might be ellipsified too.
432 * Yet to be investigated is whether this would include wordbreaking if the
433 * filename is more than 1 word and splitting if DT_EDITCONTROL was in
434 * effect. (If DT_EDITCONTROL is in effect then on occasions text will be
435 * broken within words).
436 * 4. All the stuff before the / or \, which is placed before the ellipsis.
437 */
438 static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len,
439 unsigned int *len_str, int width, SIZE *size,
440 WCHAR *modstr, ellipsis_data *pellip)
441 {
442 int len_ellipsis;
443 int len_trailing;
444 int len_under;
445 WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash;
446
447 len_ellipsis = strlenW (ELLIPSISW);
448 if (!max_len) return;
449 if (len_ellipsis >= max_len) len_ellipsis = max_len - 1;
450 if (*len_str + len_ellipsis >= max_len)
451 *len_str = max_len - len_ellipsis-1;
452 /* Hopefully this will never happen, otherwise it would probably lose
453 * the wrong character
454 */
455 str[*len_str] = '\0'; /* to simplify things */
456
457 lastBkSlash = strrchrW (str, BACK_SLASH);
458 lastFwdSlash = strrchrW (str, FORWARD_SLASH);
459 lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash;
460 if (!lastSlash) lastSlash = str;
461 len_trailing = *len_str - (lastSlash - str);
462
463 /* overlap-safe movement to the right */
464 memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR));
465 memcpy (lastSlash, ELLIPSISW, len_ellipsis*sizeof(WCHAR));
466 len_trailing += len_ellipsis;
467 /* From this point on lastSlash actually points to the ellipsis in front
468 * of the last slash and len_trailing includes the ellipsis
469 */
470
471 len_under = 0;
472 for ( ; ; )
473 {
474 if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
475 NULL, NULL, size)) break;
476
477 if (lastSlash == str || size->cx <= width) break;
478
479 /* overlap-safe movement to the left */
480 memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR));
481 lastSlash--;
482 len_under++;
483
484 assert (*len_str);
485 (*len_str)--;
486 }
487 pellip->before = lastSlash-str;
488 pellip->len = len_ellipsis;
489 pellip->under = len_under;
490 pellip->after = len_trailing - len_ellipsis;
491 *len_str += len_ellipsis;
492
493 if (modstr)
494 {
495 memcpy(modstr, str, *len_str * sizeof(WCHAR));
496 modstr[*len_str] = '\0';
497 }
498 }
499
500 /* Check the character is Chinese, Japanese, Korean and/or Thai */
501 inline BOOL IsCJKT(WCHAR wch)
502 {
503 if (0x0E00 <= wch && wch <= 0x0E7F)
504 return TRUE; /* Thai */
505
506 if (0x3000 <= wch && wch <= 0x9FFF)
507 return TRUE; /* CJK */
508
509 if (0xAC00 <= wch && wch <= 0xD7FF)
510 return TRUE; /* Korean */
511
512 if (0xFF00 <= wch && wch <= 0xFFEF)
513 return TRUE; /* CJK */
514
515 return FALSE;
516 }
517
518 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
519 static const WCHAR KinsokuClassA[] =
520 {
521 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
522 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
523 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
524 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
525 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
526 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
527 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
528 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
529 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
530 };
531
532 /*********************************************************************
533 * TEXT_WordBreak (static)
534 *
535 * Perform wordbreak processing on the given string
536 *
537 * Assumes that DT_WORDBREAK has been specified and not all the characters
538 * fit. Note that this function should even be called when the first character
539 * that doesn't fit is known to be a space or tab, so that it can swallow them.
540 *
541 * Note that the Windows processing has some strange properties.
542 * 1. If the text is left-justified and there is room for some of the spaces
543 * that follow the last word on the line then those that fit are included on
544 * the line.
545 * 2. If the text is centred or right-justified and there is room for some of
546 * the spaces that follow the last word on the line then all but one of those
547 * that fit are included on the line.
548 * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
549 * character of a new line it will be skipped.
550 *
551 * Arguments
552 * hdc [in] The handle to the DC that defines the font.
553 * str [in/out] The string that needs to be broken.
554 * max_str [in] The dimension of str (number of WCHAR).
555 * len_str [in/out] The number of characters in str
556 * width [in] The maximum width permitted
557 * format [in] The format flags in effect
558 * chars_fit [in] The maximum number of characters of str that are already
559 * known to fit; chars_fit+1 is known not to fit.
560 * chars_used [out] The number of characters of str that have been "used" and
561 * do not need to be included in later text. For example this will
562 * include any spaces that have been discarded from the start of
563 * the next line.
564 * size [out] The size of the returned text in logical coordinates
565 *
566 * Pedantic assumption - Assumes that the text length is monotonically
567 * increasing with number of characters (i.e. no weird kernings)
568 *
569 * Algorithm
570 *
571 * Work back from the last character that did fit to either a space or the last
572 * character of a word, whichever is met first.
573 * If there was one or the first character didn't fit then
574 * If the text is centred or right justified and that one character was a
575 * space then break the line before that character
576 * Otherwise break the line after that character
577 * and if the next character is a space then discard it.
578 * Suppose there was none (and the first character did fit).
579 * If Break Within Word is permitted
580 * break the word after the last character that fits (there must be
581 * at least one; none is caught earlier).
582 * Otherwise
583 * discard any trailing space.
584 * include the whole word; it may be ellipsified later
585 *
586 * Break Within Word is permitted under a set of circumstances that are not
587 * totally clear yet. Currently our best guess is:
588 * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
589 * DT_PATH_ELLIPSIS is
590 */
591
592 static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str,
593 unsigned int *len_str,
594 int width, int format, unsigned int chars_fit,
595 unsigned int *chars_used, SIZE *size)
596 {
597 WCHAR *p;
598 int word_fits;
599 assert (format & DT_WORDBREAK);
600 assert (chars_fit < *len_str);
601
602 /* Work back from the last character that did fit to either a space or the
603 * last character of a word, whichever is met first.
604 */
605 p = str + chars_fit; /* The character that doesn't fit */
606 word_fits = TRUE;
607 if (!chars_fit)
608 word_fits = FALSE;
609 else if (*p == SPACE) /* chars_fit < *len_str so this is valid */
610 p--; /* the word just fitted */
611 else
612 {
613 while (p > str && *(--p) != SPACE && (!IsCJKT(p[1]) ||
614 p[1] == L'\0' || wcschr(KinsokuClassA, p[1]) != NULL))
615 ;
616 word_fits = (p != str || *p == SPACE || IsCJKT(p[1]));
617 }
618 /* If there was one. */
619 if (word_fits)
620 {
621 int next_is_space;
622 /* break the line before/after that character */
623 if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE)
624 p++;
625 next_is_space = (unsigned int) (p - str) < *len_str && *p == SPACE;
626 *len_str = p - str;
627 /* and if the next character is a space then discard it. */
628 *chars_used = *len_str;
629 if (next_is_space)
630 (*chars_used)++;
631 }
632 /* Suppose there was none. */
633 else
634 {
635 if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) ==
636 DT_EDITCONTROL)
637 {
638 /* break the word after the last character that fits (there must be
639 * at least one). */
640 if (!chars_fit)
641 ++chars_fit;
642 *len_str = chars_fit;
643 *chars_used = chars_fit;
644
645 /* FIXME - possible error. Since the next character is now removed
646 * this could make the text longer so that it no longer fits, and
647 * so we need a loop to test and shrink.
648 */
649 }
650 /* Otherwise */
651 else
652 {
653 /* discard any trailing space. */
654 const WCHAR *e = str + *len_str;
655 p = str + chars_fit;
656 while (p < e && *p != SPACE)
657 p++;
658 *chars_used = p - str;
659 if (p < e) /* i.e. loop failed because *p == SPACE */
660 (*chars_used)++;
661
662 /* include the whole word; it may be ellipsified later */
663 *len_str = p - str;
664 /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
665 * so that it will be too long
666 */
667 }
668 }
669 /* Remeasure the string */
670 GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size);
671 }
672
673 /*********************************************************************
674 * TEXT_SkipChars
675 *
676 * Skip over the given number of characters, bearing in mind prefix
677 * substitution and the fact that a character may take more than one
678 * WCHAR (Unicode surrogates are two words long) (and there may have been
679 * a trailing &)
680 *
681 * Parameters
682 * new_count [out] The updated count
683 * new_str [out] The updated pointer
684 * start_count [in] The count of remaining characters corresponding to the
685 * start of the string
686 * start_str [in] The starting point of the string
687 * max [in] The number of characters actually in this segment of the
688 * string (the & counts)
689 * n [in] The number of characters to skip (if prefix then
690 * &c counts as one)
691 * prefix [in] Apply prefix substitution
692 *
693 * Return Values
694 * none
695 *
696 * Remarks
697 * There must be at least n characters in the string
698 * We need max because the "line" may have ended with a & followed by a tab
699 * or newline etc. which we don't want to swallow
700 */
701
702 static void TEXT_SkipChars (int *new_count, const WCHAR **new_str,
703 int start_count, const WCHAR *start_str,
704 int max, int n, int prefix)
705 {
706 /* This is specific to wide characters, MSDN doesn't say anything much
707 * about Unicode surrogates yet and it isn't clear if _wcsinc will
708 * correctly handle them so we'll just do this the easy way for now
709 */
710
711 if (prefix)
712 {
713 const WCHAR *str_on_entry = start_str;
714 assert (max >= n);
715 max -= n;
716 while (n--)
717 {
718 if ((*start_str == PREFIX || *start_str == ALPHA_PREFIX) && max--)
719 start_str++;
720 start_str++;
721 }
722 start_count -= (start_str - str_on_entry);
723 }
724 else
725 {
726 start_str += n;
727 start_count -= n;
728 }
729 *new_str = start_str;
730 *new_count = start_count;
731 }
732
733 /*********************************************************************
734 * TEXT_Reprefix
735 *
736 * Reanalyse the text to find the prefixed character. This is called when
737 * wordbreaking or ellipsification has shortened the string such that the
738 * previously noted prefixed character is no longer visible.
739 *
740 * Parameters
741 * str [in] The original string segment (including all characters)
742 * ns [in] The number of characters in str (including prefixes)
743 * pe [in] The ellipsification data
744 *
745 * Return Values
746 * The prefix offset within the new string segment (the one that contains the
747 * ellipses and does not contain the prefix characters) (-1 if none)
748 */
749
750 static int TEXT_Reprefix (const WCHAR *str, unsigned int ns,
751 const ellipsis_data *pe)
752 {
753 int result = -1;
754 unsigned int i;
755 unsigned int n = pe->before + pe->under + pe->after;
756 assert (n <= ns);
757 for (i = 0; i < n; i++, str++)
758 {
759 if (i == (unsigned int) pe->before)
760 {
761 /* Reached the path ellipsis; jump over it */
762 if (ns < (unsigned int) pe->under) break;
763 str += pe->under;
764 ns -= pe->under;
765 i += pe->under;
766 if (!pe->after) break; /* Nothing after the path ellipsis */
767 }
768 if (!ns) break;
769 ns--;
770 if (*str++ == PREFIX || *str == ALPHA_PREFIX)
771 {
772 str++;
773 if (!ns) break;
774 if (*str != PREFIX)
775 result = (i < (unsigned int) pe->before || pe->under == 0) ? i : i - pe->under + pe->len;
776 /* pe->len may be non-zero while pe_under is zero */
777 ns--;
778 }
779 }
780 return result;
781 }
782
783 /*********************************************************************
784 * Returns true if and only if the remainder of the line is a single
785 * newline representation or nothing
786 */
787
788 static int remainder_is_none_or_newline (int num_chars, const WCHAR *str)
789 {
790 if (!num_chars) return TRUE;
791 if (*str != LF && *str != CR) return FALSE;
792 if (!--num_chars) return TRUE;
793 if (*str == *(str+1)) return FALSE;
794 str++;
795 if (*str != CR && *str != LF) return FALSE;
796 if (--num_chars) return FALSE;
797 return TRUE;
798 }
799
800 /*********************************************************************
801 * Return next line of text from a string.
802 *
803 * hdc - handle to DC.
804 * str - string to parse into lines.
805 * count - length of str.
806 * dest - destination in which to return line.
807 * len - dest buffer size in chars on input, copied length into dest on output.
808 * width - maximum width of line in pixels.
809 * format - format type passed to DrawText.
810 * retsize - returned size of the line in pixels.
811 * last_line - TRUE if is the last line that will be processed
812 * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
813 * the return string is built.
814 * tabwidth - The width of a tab in logical coordinates
815 * pprefix_offset - Here is where we return the offset within dest of the first
816 * prefixed (underlined) character. -1 is returned if there
817 * are none. Note that there may be more; the calling code
818 * will need to use TEXT_Reprefix to find any later ones.
819 * pellip - Here is where we return the information about any ellipsification
820 * that was carried out. Note that if tabs are being expanded then
821 * this data will correspond to the last text segment actually
822 * returned in dest; by definition there would not have been any
823 * ellipsification in earlier text segments of the line.
824 *
825 * Returns pointer to next char in str after end of the line
826 * or NULL if end of str reached.
827 */
828 static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count,
829 WCHAR *dest, int *len, int width, DWORD format,
830 SIZE *retsize, int last_line, WCHAR **p_retstr,
831 int tabwidth, int *pprefix_offset,
832 ellipsis_data *pellip)
833 {
834 int i = 0, j = 0;
835 int plen = 0;
836 SIZE size = {0, 0};
837 int maxl = *len;
838 int seg_i, seg_count, seg_j;
839 int max_seg_width;
840 int num_fit;
841 int word_broken;
842 int line_fits;
843 unsigned int j_in_seg;
844 int ellipsified;
845 *pprefix_offset = -1;
846
847 /* For each text segment in the line */
848
849 retsize->cy = 0;
850 while (*count)
851 {
852
853 /* Skip any leading tabs */
854
855 if (str[i] == TAB && (format & DT_EXPANDTABS))
856 {
857 plen = ((plen/tabwidth)+1)*tabwidth;
858 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
859 while (*count && str[i] == TAB)
860 {
861 plen += tabwidth;
862 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
863 }
864 }
865
866
867 /* Now copy as far as the next tab or cr/lf or eos */
868
869 seg_i = i;
870 seg_count = *count;
871 seg_j = j;
872
873 while (*count &&
874 (str[i] != TAB || !(format & DT_EXPANDTABS)) &&
875 ((str[i] != CR && str[i] != LF) || (format & DT_SINGLELINE)))
876 {
877 if ((format & DT_NOPREFIX) || *count <= 1)
878 {
879 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
880 continue;
881 }
882
883 if (str[i] == PREFIX || str[i] == ALPHA_PREFIX) {
884 (*count)--, i++; /* Throw away the prefix itself */
885 if (str[i] == PREFIX)
886 {
887 /* Swallow it before we see it again */
888 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
889 }
890 else if (*pprefix_offset == -1 || *pprefix_offset >= seg_j)
891 {
892 *pprefix_offset = j;
893 }
894 /* else the previous prefix was in an earlier segment of the
895 * line; we will leave it to the drawing code to catch this
896 * one.
897 */
898 }
899 else if (str[i] == KANA_PREFIX)
900 {
901 /* Throw away katakana access keys */
902 (*count)--, i++; /* skip the prefix */
903 (*count)--, i++; /* skip the letter */
904 }
905 else
906 {
907 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
908 }
909 }
910
911
912 /* Measure the whole text segment and possibly WordBreak and
913 * ellipsify it
914 */
915
916 j_in_seg = j - seg_j;
917 max_seg_width = width - plen;
918 GetTextExtentExPointW (hdc, dest + seg_j, j_in_seg, max_seg_width, &num_fit, NULL, &size);
919
920 /* The Microsoft handling of various combinations of formats is weird.
921 * The following may very easily be incorrect if several formats are
922 * combined, and may differ between versions (to say nothing of the
923 * several bugs in the Microsoft versions).
924 */
925 word_broken = 0;
926 line_fits = (num_fit >= j_in_seg);
927 if (!line_fits && (format & DT_WORDBREAK))
928 {
929 const WCHAR *s;
930 unsigned int chars_used;
931 TEXT_WordBreak (hdc, dest+seg_j, maxl-seg_j, &j_in_seg,
932 max_seg_width, format, num_fit, &chars_used, &size);
933 line_fits = (size.cx <= max_seg_width);
934 /* and correct the counts */
935 TEXT_SkipChars (count, &s, seg_count, str+seg_i, i-seg_i,
936 chars_used, !(format & DT_NOPREFIX));
937 i = s - str;
938 word_broken = 1;
939 }
940 pellip->before = j_in_seg;
941 pellip->under = 0;
942 pellip->after = 0;
943 pellip->len = 0;
944 ellipsified = 0;
945 if (!line_fits && (format & DT_PATH_ELLIPSIS))
946 {
947 TEXT_PathEllipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg,
948 max_seg_width, &size, *p_retstr, pellip);
949 line_fits = (size.cx <= max_seg_width);
950 ellipsified = 1;
951 }
952 /* NB we may end up ellipsifying a word-broken or path_ellipsified
953 * string */
954 if ((!line_fits && (format & DT_WORD_ELLIPSIS)) ||
955 ((format & DT_END_ELLIPSIS) &&
956 ((last_line && *count) ||
957 (remainder_is_none_or_newline (*count, &str[i]) && !line_fits))))
958 {
959 int before, len_ellipsis;
960 TEXT_Ellipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg,
961 max_seg_width, &size, *p_retstr, &before, &len_ellipsis);
962 if (before > pellip->before)
963 {
964 /* We must have done a path ellipsis too */
965 pellip->after = before - pellip->before - pellip->len;
966 /* Leave the len as the length of the first ellipsis */
967 }
968 else
969 {
970 /* If we are here after a path ellipsification it must be
971 * because even the ellipsis itself didn't fit.
972 */
973 assert (pellip->under == 0 && pellip->after == 0);
974 pellip->before = before;
975 pellip->len = len_ellipsis;
976 /* pellip->after remains as zero as does
977 * pellip->under
978 */
979 }
980 line_fits = (size.cx <= max_seg_width);
981 ellipsified = 1;
982 }
983 /* As an optimisation if we have ellipsified and we are expanding
984 * tabs and we haven't reached the end of the line we can skip to it
985 * now rather than going around the loop again.
986 */
987 if ((format & DT_EXPANDTABS) && ellipsified)
988 {
989 if (format & DT_SINGLELINE)
990 *count = 0;
991 else
992 {
993 while ((*count) && str[i] != CR && str[i] != LF)
994 {
995 (*count)--, i++;
996 }
997 }
998 }
999
1000 j = seg_j + j_in_seg;
1001 if (*pprefix_offset >= seg_j + pellip->before)
1002 {
1003 *pprefix_offset = TEXT_Reprefix (str + seg_i, i - seg_i, pellip);
1004 if (*pprefix_offset != -1)
1005 *pprefix_offset += seg_j;
1006 }
1007
1008 plen += size.cx;
1009 if (size.cy > retsize->cy)
1010 retsize->cy = size.cy;
1011
1012 if (word_broken)
1013 break;
1014 else if (!*count)
1015 break;
1016 else if (str[i] == CR || str[i] == LF)
1017 {
1018 (*count)--, i++;
1019 if (*count && (str[i] == CR || str[i] == LF) && str[i] != str[i-1])
1020 {
1021 (*count)--, i++;
1022 }
1023 break;
1024 }
1025 /* else it was a Tab and we go around again */
1026 }
1027
1028 retsize->cx = plen;
1029 *len = j;
1030 if (*count)
1031 return (&str[i]);
1032 else
1033 return NULL;
1034 }
1035
1036
1037 /***********************************************************************
1038 * TEXT_DrawUnderscore
1039 *
1040 * Draw the underline under the prefixed character
1041 *
1042 * Parameters
1043 * hdc [in] The handle of the DC for drawing
1044 * x [in] The x location of the line segment (logical coordinates)
1045 * y [in] The y location of where the underscore should appear
1046 * (logical coordinates)
1047 * str [in] The text of the line segment
1048 * offset [in] The offset of the underscored character within str
1049 * rect [in] Clipping rectangle (if not NULL)
1050 */
1051 /* Synced with wine 1.1.32 */
1052 static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset, const RECT *rect)
1053 {
1054 int prefix_x;
1055 int prefix_end;
1056 SIZE size;
1057 HPEN hpen;
1058 HPEN oldPen;
1059
1060 GetTextExtentPointW (hdc, str, offset, &size);
1061 prefix_x = x + size.cx;
1062 GetTextExtentPointW (hdc, str, offset+1, &size);
1063 prefix_end = x + size.cx - 1;
1064 /* The above method may eventually be slightly wrong due to kerning etc. */
1065
1066 /* Check for clipping */
1067 if (rect)
1068 {
1069 if (prefix_x > rect->right || prefix_end < rect->left ||
1070 y < rect->top || y > rect->bottom)
1071 return; /* Completely outside */
1072 /* Partially outside */
1073 if (prefix_x < rect->left ) prefix_x = rect->left;
1074 if (prefix_end > rect->right) prefix_end = rect->right;
1075 }
1076
1077 hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc));
1078 oldPen = SelectObject (hdc, hpen);
1079 MoveToEx (hdc, prefix_x, y, NULL);
1080 LineTo (hdc, prefix_end, y);
1081 SelectObject (hdc, oldPen);
1082 DeleteObject (hpen);
1083 }
1084
1085 /***********************************************************************
1086 * DrawTextExW (USER32.@)
1087 *
1088 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1089 * is not quite complete, especially with regard to \0. We will assume that
1090 * the returned string could have a length of up to i_count+3 and also have
1091 * a trailing \0 (which would be 4 more than a not-null-terminated string but
1092 * 3 more than a null-terminated string). If this is not so then increase
1093 * the allowance in DrawTextExA.
1094 */
1095 #define MAX_BUFFER 1024
1096 /*
1097 * @implemented
1098 *
1099 * Synced with wine 1.1.32
1100 */
1101 INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
1102 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
1103 {
1104 SIZE size;
1105 const WCHAR *strPtr;
1106 WCHAR *retstr, *p_retstr;
1107 size_t size_retstr;
1108 WCHAR line[MAX_BUFFER];
1109 int len, lh, count=i_count;
1110 TEXTMETRICW tm;
1111 int lmargin = 0, rmargin = 0;
1112 int x = rect->left, y = rect->top;
1113 int width = rect->right - rect->left;
1114 int max_width = 0;
1115 int last_line;
1116 int tabwidth /* to keep gcc happy */ = 0;
1117 int prefix_offset;
1118 ellipsis_data ellip;
1119 int invert_y=0;
1120
1121 TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str, count), count,
1122 wine_dbgstr_rect(rect), flags);
1123
1124 if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1125 dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin);
1126
1127 if (!str) return 0;
1128
1129 strPtr = str;
1130
1131 if (flags & DT_SINGLELINE)
1132 flags &= ~DT_WORDBREAK;
1133
1134 GetTextMetricsW(hdc, &tm);
1135 if (flags & DT_EXTERNALLEADING)
1136 lh = tm.tmHeight + tm.tmExternalLeading;
1137 else
1138 lh = tm.tmHeight;
1139
1140 if (str[0] && count == 0)
1141 return lh;
1142
1143 if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS))
1144 return 0;
1145
1146 if (count == -1)
1147 {
1148 count = strlenW(str);
1149 if (count == 0)
1150 {
1151 if( flags & DT_CALCRECT)
1152 {
1153 rect->right = rect->left;
1154 if( flags & DT_SINGLELINE)
1155 rect->bottom = rect->top + lh;
1156 else
1157 rect->bottom = rect->top;
1158 }
1159 return lh;
1160 }
1161 }
1162
1163 if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1164 {
1165 SIZE window_ext, viewport_ext;
1166 GetWindowExtEx(hdc, &window_ext);
1167 GetViewportExtEx(hdc, &viewport_ext);
1168 if ((window_ext.cy > 0) != (viewport_ext.cy > 0))
1169 invert_y = 1;
1170 }
1171
1172 if (dtp)
1173 {
1174 lmargin = dtp->iLeftMargin;
1175 rmargin = dtp->iRightMargin;
1176 if (!(flags & (DT_CENTER | DT_RIGHT)))
1177 x += lmargin;
1178 dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */
1179 }
1180
1181 if (flags & DT_EXPANDTABS)
1182 {
1183 int tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8;
1184 tabwidth = tm.tmAveCharWidth * tabstop;
1185 }
1186
1187 if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
1188
1189 if (flags & DT_MODIFYSTRING)
1190 {
1191 size_retstr = (count + 4) * sizeof (WCHAR);
1192 retstr = HeapAlloc(GetProcessHeap(), 0, size_retstr);
1193 if (!retstr) return 0;
1194 memcpy (retstr, str, size_retstr);
1195 }
1196 else
1197 {
1198 size_retstr = 0;
1199 retstr = NULL;
1200 }
1201 p_retstr = retstr;
1202
1203 do
1204 {
1205 len = sizeof(line)/sizeof(line[0]);
1206 if (invert_y)
1207 last_line = !(flags & DT_NOCLIP) && y - ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) < rect->bottom;
1208 else
1209 last_line = !(flags & DT_NOCLIP) && y + ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) > rect->bottom;
1210 strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags, &size, last_line, &p_retstr, tabwidth, &prefix_offset, &ellip);
1211
1212 if (flags & DT_CENTER) x = (rect->left + rect->right -
1213 size.cx) / 2;
1214 else if (flags & DT_RIGHT) x = rect->right - size.cx;
1215
1216 if (flags & DT_SINGLELINE)
1217 {
1218 if (flags & DT_VCENTER) y = rect->top +
1219 (rect->bottom - rect->top) / 2 - size.cy / 2;
1220 else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
1221 }
1222
1223 if (!(flags & DT_CALCRECT))
1224 {
1225 const WCHAR *str = line;
1226 int xseg = x;
1227 while (len)
1228 {
1229 int len_seg;
1230 SIZE size;
1231 if ((flags & DT_EXPANDTABS))
1232 {
1233 const WCHAR *p;
1234 p = str; while (p < str+len && *p != TAB) p++;
1235 len_seg = p - str;
1236 if (len_seg != len && !GetTextExtentPointW(hdc, str, len_seg, &size))
1237 return 0;
1238 }
1239 else
1240 len_seg = len;
1241
1242 if (!ExtTextOutW( hdc, xseg, y,
1243 ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) |
1244 ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0),
1245 rect, str, len_seg, NULL )) return 0;
1246 if (prefix_offset != -1 && prefix_offset < len_seg)
1247 {
1248 TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset, (flags & DT_NOCLIP) ? NULL : rect);
1249 }
1250 len -= len_seg;
1251 str += len_seg;
1252 if (len)
1253 {
1254 assert ((flags & DT_EXPANDTABS) && *str == TAB);
1255 len--; str++;
1256 xseg += ((size.cx/tabwidth)+1)*tabwidth;
1257 if (prefix_offset != -1)
1258 {
1259 if (prefix_offset < len_seg)
1260 {
1261 /* We have just drawn an underscore; we ought to
1262 * figure out where the next one is. I am going
1263 * to leave it for now until I have a better model
1264 * for the line, which will make reprefixing easier.
1265 * This is where ellip would be used.
1266 */
1267 prefix_offset = -1;
1268 }
1269 else
1270 prefix_offset -= len_seg;
1271 }
1272 }
1273 }
1274 }
1275 else if (size.cx > max_width)
1276 max_width = size.cx;
1277
1278 if (invert_y)
1279 y -= lh;
1280 else
1281 y += lh;
1282 if (dtp)
1283 dtp->uiLengthDrawn += len;
1284 }
1285 while (strPtr && !last_line);
1286
1287 if (flags & DT_CALCRECT)
1288 {
1289 rect->right = rect->left + max_width;
1290 rect->bottom = y;
1291 if (dtp)
1292 rect->right += lmargin + rmargin;
1293 }
1294 if (retstr)
1295 {
1296 memcpy (str, retstr, size_retstr);
1297 HeapFree (GetProcessHeap(), 0, retstr);
1298 }
1299 return y - rect->top;
1300 }
1301
1302 /***********************************************************************
1303 * DrawTextExA (USER32.@)
1304 *
1305 * If DT_MODIFYSTRING is specified then there must be room for up to
1306 * 4 extra characters. We take great care about just how much modified
1307 * string we return.
1308 *
1309 * @implemented
1310 *
1311 * Synced with wine 1.1.32
1312 */
1313 INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
1314 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
1315 {
1316 WCHAR *wstr;
1317 WCHAR *p;
1318 INT ret = 0;
1319 int i;
1320 DWORD wcount;
1321 DWORD wmax;
1322 DWORD amax;
1323 UINT cp;
1324
1325 if (!count) return 0;
1326 if (!str && count > 0) return 0;
1327 if( !str || ((count == -1) && !(count = strlen(str))))
1328 {
1329 int lh;
1330 TEXTMETRICA tm;
1331
1332 if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS))
1333 return 0;
1334
1335 GetTextMetricsA(hdc, &tm);
1336 if (flags & DT_EXTERNALLEADING)
1337 lh = tm.tmHeight + tm.tmExternalLeading;
1338 else
1339 lh = tm.tmHeight;
1340
1341 if( flags & DT_CALCRECT)
1342 {
1343 rect->right = rect->left;
1344 if( flags & DT_SINGLELINE)
1345 rect->bottom = rect->top + lh;
1346 else
1347 rect->bottom = rect->top;
1348 }
1349 return lh;
1350 }
1351 cp = GdiGetCodePage( hdc );
1352 wcount = MultiByteToWideChar( cp, 0, str, count, NULL, 0 );
1353 wmax = wcount;
1354 amax = count;
1355 if (flags & DT_MODIFYSTRING)
1356 {
1357 wmax += 4;
1358 amax += 4;
1359 }
1360 wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
1361 if (wstr)
1362 {
1363 MultiByteToWideChar( cp, 0, str, count, wstr, wcount );
1364 if (flags & DT_MODIFYSTRING)
1365 for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE;
1366 /* Initialise the extra characters so that we can see which ones
1367 * change. U+FFFE is guaranteed to be not a unicode character and
1368 * so will not be generated by DrawTextEx itself.
1369 */
1370 ret = DrawTextExW( hdc, wstr, wcount, rect, flags, dtp );
1371 if (flags & DT_MODIFYSTRING)
1372 {
1373 /* Unfortunately the returned string may contain multiple \0s
1374 * and so we need to measure it ourselves.
1375 */
1376 for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
1377 WideCharToMultiByte( cp, 0, wstr, wcount, str, amax, NULL, NULL );
1378 }
1379 HeapFree(GetProcessHeap(), 0, wstr);
1380 }
1381 return ret;
1382 }
1383
1384 /***********************************************************************
1385 * DrawTextW (USER32.@)
1386 *
1387 * @implemented
1388 */
1389 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
1390 {
1391 DRAWTEXTPARAMS dtp;
1392
1393 memset (&dtp, 0, sizeof(dtp));
1394 dtp.cbSize = sizeof(dtp);
1395 if (flags & DT_TABSTOP)
1396 {
1397 dtp.iTabLength = (flags >> 8) & 0xff;
1398 flags &= 0xffff00ff;
1399 }
1400 return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
1401 }
1402
1403 /***********************************************************************
1404 * DrawTextA (USER32.@)
1405 *
1406 * @implemented
1407 */
1408 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
1409 {
1410 DRAWTEXTPARAMS dtp;
1411
1412 memset (&dtp, 0, sizeof(dtp));
1413 dtp.cbSize = sizeof(dtp);
1414 if (flags & DT_TABSTOP)
1415 {
1416 dtp.iTabLength = (flags >> 8) & 0xff;
1417 flags &= 0xffff00ff;
1418 }
1419 return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
1420 }