Release 940420
[wine] / objects / text.c
1 /*
2  * text functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  */
6
7 static char Copyright[] = "Copyright  Alexandre Julliard, 1993";
8
9 #include <X11/Xatom.h>
10 #include "windows.h"
11 #include "gdi.h"
12
13 #define TAB     9
14 #define LF     10
15 #define CR     13
16 #define SPACE  32
17 #define PREFIX 38
18
19 static int tabstop = 8;
20 static int tabwidth;
21 static int spacewidth;
22 static int prefix_offset;
23
24
25 static char *TEXT_NextLine(HDC hdc, char *str, int *count, char *dest, 
26                            int *len, int width, WORD format)
27 {
28     /* Return next line of text from a string.
29      * 
30      * hdc - handle to DC.
31      * str - string to parse into lines.
32      * count - length of str.
33      * dest - destination in which to return line.
34      * len - length of resultant line in dest in chars.
35      * width - maximum width of line in pixels.
36      * format - format type passed to DrawText.
37      *
38      * Returns pointer to next char in str after end of the line
39      * or NULL if end of str reached.
40      */
41
42     int i = 0, j = 0, k;
43     int plen = 0;
44     int numspaces;
45     SIZE size;
46     int lasttab = 0;
47     int wb_i = 0, wb_j = 0, wb_count;
48
49     while (*count)
50     {
51         switch (str[i])
52         {
53         case CR:
54         case LF:
55             if (!(format & DT_SINGLELINE))
56             {
57                 i++;
58                 if (str[i] == CR || str[i] == LF)
59                     i++;
60                 *len = j;
61                 return (&str[i]);
62             }
63             dest[j++] = str[i++];
64             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX))
65             {
66                 if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
67                     return NULL;
68                 plen += size.cx;
69             }
70             break;
71
72         case PREFIX:
73             if (!(format & DT_NOPREFIX))
74             {
75                 prefix_offset = j + 1;
76                 i++;
77             }
78             else
79             {
80                 dest[j++] = str[i++];
81                 if (!(format & DT_NOCLIP))
82                 {
83                     if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
84                         return NULL;
85                     plen += size.cx;
86                 }
87             }
88             break;
89
90         case TAB:
91             if (format & DT_EXPANDTABS)
92             {
93                 wb_i = ++i;
94                 wb_j = j;
95                 wb_count = *count;
96
97                 if (!GetTextExtentPoint(hdc, &dest[lasttab], j - lasttab,
98                                                                  &size))
99                     return NULL;
100
101                 numspaces = (tabwidth - size.cx) / spacewidth;
102                 for (k = 0; k < numspaces; k++)
103                     dest[j++] = SPACE;
104                 plen += tabwidth - size.cx;
105                 lasttab = wb_j + numspaces;
106             }
107             else
108             {
109                 dest[j++] = str[i++];
110                 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX))
111                 {
112                     if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
113                         return NULL;
114                     plen += size.cx;
115                 }
116             }
117             break;
118
119         case SPACE:
120             dest[j++] = str[i++];
121             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX))
122             {
123                 wb_i = i;
124                 wb_j = j - 1;
125                 wb_count = *count;
126                 if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
127                     return NULL;
128                 plen += size.cx;
129             }
130             break;
131
132         default:
133             dest[j++] = str[i++];
134             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX))
135             {
136                 if (!GetTextExtentPoint(hdc, &dest[j-1], 1, &size))
137                     return NULL;
138                 plen += size.cx;
139             }
140         }
141
142         (*count)--;
143         if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
144         {
145             if (plen > width)
146             {
147                 if (format & DT_WORDBREAK)
148                 {
149                     *len = wb_j;
150                     *count = wb_count;
151                     return (&str[wb_i]);
152                 }
153                 else
154                 {
155                     *len = j;
156                     return (&str[i]);
157                 }
158             }
159         }
160     }
161     
162     *len = j;
163     return NULL;
164 }
165
166
167 /***********************************************************************
168  *           DrawText    (USER.85)
169  */
170 int DrawText( HDC hdc, LPSTR str, int count, LPRECT rect, WORD flags )
171 {
172     SIZE size;
173     char *strPtr;
174     static char line[1024];
175     int len, lh, prefix_x, prefix_len;
176     TEXTMETRIC tm;
177     int x = rect->left, y = rect->top;
178     int width = rect->right - rect->left;
179
180     if (count == -1) count = strlen(str);
181     strPtr = str;
182
183     GetTextMetrics(hdc, &tm);
184     if (flags & DT_EXTERNALLEADING)
185         lh = tm.tmHeight + tm.tmExternalLeading;
186     else
187         lh = tm.tmHeight;
188
189     if (flags & DT_TABSTOP)
190         tabstop = flags >> 8;
191
192     if (flags & DT_EXPANDTABS)
193     {
194         GetTextExtentPoint(hdc, " ", 1, &size);
195         spacewidth = size.cx;
196         GetTextExtentPoint(hdc, "o", 1, &size);
197         tabwidth = size.cx * tabstop;
198     }
199
200     do
201     {
202         prefix_offset = -1;
203         strPtr = TEXT_NextLine(hdc, strPtr, &count, line, &len, width, flags);
204
205         if (prefix_offset != -1)
206         {
207             GetTextExtentPoint(hdc, line, prefix_offset - 1, &size);
208             prefix_x = size.cx;
209             GetTextExtentPoint(hdc, line + prefix_offset, 1, &size);
210             prefix_len = size.cx;
211         }
212
213         if (!GetTextExtentPoint(hdc, line, len, &size)) return 0;
214         if (flags & DT_CENTER) x = (rect->left + rect->right -
215                                     size.cx) / 2;
216         else if (flags & DT_RIGHT) x = rect->right - size.cx;
217
218         if (flags & DT_SINGLELINE)
219         {
220             if (flags & DT_VCENTER) y = rect->top + 
221                 (rect->bottom - rect->top) / 2 - size.cy / 2;
222             else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
223         }
224         if (!(flags & DT_CALCRECT))
225             if (!TextOut(hdc, x, y, line, len)) return 0;
226         if (prefix_offset != -1)
227         {
228             MoveTo(hdc, x + prefix_x, y + size.cy);
229             LineTo(hdc, x + prefix_x + prefix_len, y + size.cy);
230         }
231
232         if (strPtr)
233         {
234             y += lh;
235             if (!(flags & DT_NOCLIP))
236             {
237                 if (y > rect->bottom - lh)
238                     break;
239             }
240         }
241     }
242     while (strPtr);
243     if (flags & DT_CALCRECT) rect->bottom = y;
244     return 1;
245 }
246
247
248 /***********************************************************************
249  *           TextOut    (GDI.33)
250  */
251 BOOL TextOut( HDC hdc, short x, short y, LPSTR str, short count )
252 {
253     int dir, ascent, descent, i;
254     XCharStruct info;
255     XFontStruct *font;
256
257     DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
258     if (!dc) return FALSE;
259     if (!DC_SetupGCForText( dc )) return TRUE;
260     font = dc->u.x.font.fstruct;
261
262     if (dc->w.textAlign & TA_UPDATECP)
263     {
264         x = dc->w.CursPosX;
265         y = dc->w.CursPosY;
266     }
267 #ifdef DEBUG_TEXT
268     printf( "TextOut: %d,%d '%s', %d\n", x, y, str, count );
269 #endif
270     x = XLPTODP( dc, x );
271     y = YLPTODP( dc, y );
272
273     XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
274     info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
275
276       /* Compute starting position */
277
278     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
279     {
280       case TA_LEFT:
281           if (dc->w.textAlign & TA_UPDATECP)
282               dc->w.CursPosX = XDPTOLP( dc, x + info.width );
283           break;
284       case TA_RIGHT:
285           x -= info.width;
286           if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
287           break;
288       case TA_CENTER:
289           x -= info.width / 2;
290           break;
291     }
292     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
293     {
294       case TA_TOP:
295           y += font->ascent;
296           break;
297       case TA_BOTTOM:
298           y -= font->descent;
299           break;
300       case TA_BASELINE:
301           break;
302     }
303
304       /* Draw text */
305
306     if (!dc->w.charExtra && !dc->w.breakExtra)
307     {
308         if (dc->w.backgroundMode == TRANSPARENT)
309             XDrawString( XT_display, dc->u.x.drawable, dc->u.x.gc, 
310                          dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
311         else
312             XDrawImageString( XT_display, dc->u.x.drawable, dc->u.x.gc,
313                               dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
314     }
315     else
316     {
317         char * p = str;
318         int xchar = x;
319         for (i = 0; i < count; i++, p++)
320         {
321             XCharStruct * charStr;
322             unsigned char ch = *p;
323             int extraWidth;
324             
325             if ((ch < font->min_char_or_byte2)||(ch > font->max_char_or_byte2))
326                 ch = font->default_char;
327             if (!font->per_char) charStr = &font->min_bounds;
328             else charStr = font->per_char + ch - font->min_char_or_byte2;
329
330             extraWidth = dc->w.charExtra;
331             if (ch == dc->u.x.font.metrics.tmBreakChar)
332                 extraWidth += dc->w.breakExtra;
333
334             if (dc->w.backgroundMode == TRANSPARENT)
335                 XDrawString( XT_display, dc->u.x.drawable, dc->u.x.gc,
336                              dc->w.DCOrgX + xchar, dc->w.DCOrgY + y, p, 1 );
337             else
338             {
339                 XDrawImageString( XT_display, dc->u.x.drawable, dc->u.x.gc,
340                                   dc->w.DCOrgX + xchar, dc->w.DCOrgY + y, p, 1 );
341                 XSetForeground( XT_display, dc->u.x.gc, dc->w.backgroundPixel);
342                 XFillRectangle( XT_display, dc->u.x.drawable, dc->u.x.gc,
343                                 dc->w.DCOrgX + xchar + charStr->width,
344                                 dc->w.DCOrgY + y - font->ascent,
345                                 extraWidth, font->ascent + font->descent );
346                 XSetForeground( XT_display, dc->u.x.gc, dc->w.textPixel );
347             }
348             xchar += charStr->width + extraWidth;
349         }
350     }
351
352       /* Draw underline and strike-out if needed */
353
354     if (dc->u.x.font.metrics.tmUnderlined)
355     {
356         long linePos, lineWidth;       
357         if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
358             linePos = font->descent-1;
359         if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
360             lineWidth = 0;
361         else if (lineWidth == 1) lineWidth = 0;
362         XSetLineAttributes( XT_display, dc->u.x.gc, lineWidth,
363                             LineSolid, CapRound, JoinBevel ); 
364         XDrawLine( XT_display, dc->u.x.drawable, dc->u.x.gc,
365                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
366                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
367     }
368     if (dc->u.x.font.metrics.tmStruckOut)
369     {
370         long lineAscent, lineDescent;
371         if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
372             lineAscent = font->ascent / 3;
373         if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
374             lineDescent = -lineAscent;
375         XSetLineAttributes( XT_display, dc->u.x.gc, lineAscent + lineDescent,
376                             LineSolid, CapRound, JoinBevel ); 
377         XDrawLine( XT_display, dc->u.x.drawable, dc->u.x.gc,
378                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
379                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
380     }
381     
382     return TRUE;
383 }
384
385 /***********************************************************************
386  *              GrayString (USER.185)
387  */
388 BOOL GrayString(HDC hdc, HBRUSH hbr, FARPROC gsprc, LPARAM lParam, 
389                 INT cch, INT x, INT y, INT cx, INT cy)
390 {
391         int s, current_color;
392
393         if (gsprc) {
394                 return CallGrayStringProc(gsprc, hdc, lParam, 
395                                         cch ? cch : lstrlen((LPCSTR) lParam) );
396         } else {
397                 current_color = GetTextColor(hdc);
398                 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT) );
399                 s = TextOut(hdc, x, y, (LPSTR) lParam, 
400                                 cch ? cch : lstrlen((LPCSTR) lParam) );
401                 SetTextColor(hdc, current_color);
402                 
403                 return s;
404         }
405 }