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