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