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