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