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