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