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