Release 940722
[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;
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_end;
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, &size);
218             prefix_x = size.cx;
219             GetTextExtentPoint(hdc, line, prefix_offset + 1, &size);
220             prefix_end = size.cx - 1;
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             HPEN hpen = CreatePen( PS_SOLID, 1, GetTextColor(hdc) );
239             HPEN oldPen = SelectObject( hdc, hpen );
240             MoveTo(hdc, x + prefix_x, y + tm.tmAscent + 1 );
241             LineTo(hdc, x + prefix_end, y + tm.tmAscent + 1 );
242             SelectObject( hdc, oldPen );
243             DeleteObject( hpen );
244         }
245
246         if (strPtr)
247         {
248             y += lh;
249             if (!(flags & DT_NOCLIP))
250             {
251                 if (y > rect->bottom - lh)
252                     break;
253             }
254         }
255     }
256     while (strPtr);
257     if (flags & DT_CALCRECT) rect->bottom = y;
258     return 1;
259 }
260
261
262 /***********************************************************************
263  *           TextOut    (GDI.33)
264  */
265 BOOL TextOut( HDC hdc, short x, short y, LPSTR str, short count )
266 {
267     int dir, ascent, descent, i;
268     XCharStruct info;
269     XFontStruct *font;
270
271     DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
272     if (!dc) 
273     {
274         dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
275         if (!dc) return FALSE;
276         MF_TextOut(dc, x, y, str, count);
277         return TRUE;
278     }
279
280     if (!DC_SetupGCForText( dc )) return TRUE;
281     font = dc->u.x.font.fstruct;
282
283     if (dc->w.textAlign & TA_UPDATECP)
284     {
285         x = dc->w.CursPosX;
286         y = dc->w.CursPosY;
287     }
288 #ifdef DEBUG_TEXT
289     printf( "TextOut: %d,%d '%s', %d\n", x, y, str, count );
290 #endif
291     x = XLPTODP( dc, x );
292     y = YLPTODP( dc, y );
293
294     XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
295     info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
296
297       /* Compute starting position */
298
299     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
300     {
301       case TA_LEFT:
302           if (dc->w.textAlign & TA_UPDATECP)
303               dc->w.CursPosX = XDPTOLP( dc, x + info.width );
304           break;
305       case TA_RIGHT:
306           x -= info.width;
307           if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
308           break;
309       case TA_CENTER:
310           x -= info.width / 2;
311           break;
312     }
313     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
314     {
315       case TA_TOP:
316           y += font->ascent;
317           break;
318       case TA_BOTTOM:
319           y -= font->descent;
320           break;
321       case TA_BASELINE:
322           break;
323     }
324
325       /* Draw text */
326
327     if (!dc->w.charExtra && !dc->w.breakExtra)
328     {
329         if (dc->w.backgroundMode == TRANSPARENT)
330             XDrawString( XT_display, dc->u.x.drawable, dc->u.x.gc, 
331                          dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
332         else
333             XDrawImageString( XT_display, dc->u.x.drawable, dc->u.x.gc,
334                               dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
335     }
336     else
337     {
338         char * p = str;
339         int xchar = x;
340         for (i = 0; i < count; i++, p++)
341         {
342             XCharStruct * charStr;
343             unsigned char ch = *p;
344             int extraWidth;
345             
346             if ((ch < font->min_char_or_byte2)||(ch > font->max_char_or_byte2))
347                 ch = font->default_char;
348             if (!font->per_char) charStr = &font->min_bounds;
349             else charStr = font->per_char + ch - font->min_char_or_byte2;
350
351             extraWidth = dc->w.charExtra;
352             if (ch == dc->u.x.font.metrics.tmBreakChar)
353                 extraWidth += dc->w.breakExtra;
354
355             if (dc->w.backgroundMode == TRANSPARENT)
356                 XDrawString( XT_display, dc->u.x.drawable, dc->u.x.gc,
357                              dc->w.DCOrgX + xchar, dc->w.DCOrgY + y, p, 1 );
358             else
359             {
360                 XDrawImageString( XT_display, dc->u.x.drawable, dc->u.x.gc,
361                                   dc->w.DCOrgX + xchar, dc->w.DCOrgY + y, p, 1 );
362                 XSetForeground( XT_display, dc->u.x.gc, dc->w.backgroundPixel);
363                 XFillRectangle( XT_display, dc->u.x.drawable, dc->u.x.gc,
364                                 dc->w.DCOrgX + xchar + charStr->width,
365                                 dc->w.DCOrgY + y - font->ascent,
366                                 extraWidth, font->ascent + font->descent );
367                 XSetForeground( XT_display, dc->u.x.gc, dc->w.textPixel );
368             }
369             xchar += charStr->width + extraWidth;
370         }
371     }
372
373       /* Draw underline and strike-out if needed */
374
375     if (dc->u.x.font.metrics.tmUnderlined)
376     {
377         long linePos, lineWidth;       
378         if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
379             linePos = font->descent-1;
380         if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
381             lineWidth = 0;
382         else if (lineWidth == 1) lineWidth = 0;
383         XSetLineAttributes( XT_display, dc->u.x.gc, lineWidth,
384                             LineSolid, CapRound, JoinBevel ); 
385         XDrawLine( XT_display, dc->u.x.drawable, dc->u.x.gc,
386                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
387                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
388     }
389     if (dc->u.x.font.metrics.tmStruckOut)
390     {
391         long lineAscent, lineDescent;
392         if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
393             lineAscent = font->ascent / 3;
394         if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
395             lineDescent = -lineAscent;
396         XSetLineAttributes( XT_display, dc->u.x.gc, lineAscent + lineDescent,
397                             LineSolid, CapRound, JoinBevel ); 
398         XDrawLine( XT_display, dc->u.x.drawable, dc->u.x.gc,
399                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
400                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
401     }
402     
403     return TRUE;
404 }
405
406 /***********************************************************************
407  *              GrayString (USER.185)
408  */
409 BOOL GrayString(HDC hdc, HBRUSH hbr, FARPROC gsprc, LPARAM lParam, 
410                 INT cch, INT x, INT y, INT cx, INT cy)
411 {
412         int s, current_color;
413
414         if (gsprc) {
415                 return CallGrayStringProc(gsprc, hdc, lParam, 
416                                         cch ? cch : lstrlen((LPCSTR) lParam) );
417         } else {
418                 current_color = GetTextColor(hdc);
419                 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT) );
420                 s = TextOut(hdc, x, y, (LPSTR) lParam, 
421                                 cch ? cch : lstrlen((LPCSTR) lParam) );
422                 SetTextColor(hdc, current_color);
423                 
424                 return s;
425         }
426 }
427
428 /***********************************************************************
429  *                      TabbedTextOut           [USER.196]
430  */
431 LONG TabbedTextOut(HDC hDC, short x, short y, LPSTR lpStr, short nCount, 
432                 short nTabCount, LPINT lpTabPos, short nTabOrg)
433 {
434         WORD    width, height;
435         printf("EMPTY STUB !!! TabbedTextOut(); ! call TextOut() for now !\n");
436         height = HIWORD(GetTextExtent(hDC, lpStr, nCount));
437         width = LOWORD(GetTextExtent(hDC, lpStr, nCount));
438         TextOut(hDC, x, y, lpStr, nCount);
439         return MAKELONG(width, height);
440 }
441
442
443 /***********************************************************************
444  *                      ExtTextOut                      [GDI.351]
445  */
446 BOOL ExtTextOut(HDC hDC, short x, short y, WORD wOptions, LPRECT lprect,
447                         LPSTR str, WORD count, LPINT lpDx)
448 {
449         printf("EMPTY STUB !!! ExtTextOut(); ! call TextOut() for now !\n");
450         TextOut(hDC, x, y, str, count);
451         return FALSE;
452 }
453
454