- New implementation of SendMessage, ReceiveMessage, ReplyMessage functions
[wine] / graphics / x11drv / text.c
1 /*
2  * X11 graphics driver text functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  */
6
7 #include "config.h"
8
9 #ifndef X_DISPLAY_MISSING
10
11 #include <X11/Xatom.h>
12 #include "ts_xlib.h"
13
14 #include <stdlib.h>
15 #include "wintypes.h"
16 #include <math.h>
17 #include "dc.h"
18 #include "gdi.h"
19 #include "heap.h"
20 #include "x11font.h"
21 #include "debug.h"
22
23 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
24 #define IROUND(x) (int)((x)>0? (x)+0.5 : (x) - 0.5)
25
26 /***********************************************************************
27  *           X11DRV_ExtTextOut
28  */
29 BOOL32
30 X11DRV_ExtTextOut( DC *dc, INT32 x, INT32 y, UINT32 flags,
31                    const RECT32 *lprect, LPCSTR str, UINT32 count,
32                    const INT32 *lpDx )
33 {
34     int                 i;
35     fontObject*         pfo;
36     INT32               width, ascent, descent, xwidth, ywidth;
37     XFontStruct*        font;
38     RECT32              rect;
39     char                dfBreakChar, lfUnderline, lfStrikeOut;
40     BOOL32              rotated = FALSE;
41     X11DRV_PDEVICE      *physDev = (X11DRV_PDEVICE *)dc->physDev;
42
43     if (!X11DRV_SetupGCForText( dc )) return TRUE;
44
45     pfo = XFONT_GetFontObject( physDev->font );
46     font = pfo->fs;
47      
48     if (pfo->lf.lfEscapement && pfo->lpX11Trans)
49         rotated = TRUE;
50     dfBreakChar = (char)pfo->fi->df.dfBreakChar;
51     lfUnderline = (pfo->fo_flags & FO_SYNTH_UNDERLINE) ? 1 : 0;
52     lfStrikeOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT) ? 1 : 0;
53
54     TRACE(text,"hdc=%04x df=%04x %d,%d %s, %d  flags=%d lpDx=%p\n",
55           dc->hSelf, (UINT16)(physDev->font), x, y,
56           debugstr_an (str, count), count, flags, lpDx);
57
58     /* some strings sent here end in a newline for whatever reason.  I have no
59        clue what the right treatment should be in general, but ignoring
60        terminating newlines seems ok.  MW, April 1998.  */
61     if (count > 0 && str[count - 1] == '\n') count--;
62
63     if (lprect != NULL) TRACE(text, "\trect=(%d,%d - %d,%d)\n",
64                                      lprect->left, lprect->top,
65                                      lprect->right, lprect->bottom );
66       /* Setup coordinates */
67
68     if (dc->w.textAlign & TA_UPDATECP)
69     {
70         x = dc->w.CursPosX;
71         y = dc->w.CursPosY;
72     }
73
74     if (flags & (ETO_OPAQUE | ETO_CLIPPED))  /* there's a rectangle */
75     {
76         if (!lprect)  /* not always */
77         {
78             SIZE32 sz;
79             if (flags & ETO_CLIPPED)  /* Can't clip with no rectangle */
80               return FALSE;
81             if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
82               return FALSE;
83             rect.left   = XLPTODP( dc, x );
84             rect.right  = XLPTODP( dc, x+sz.cx );
85             rect.top    = YLPTODP( dc, y );
86             rect.bottom = YLPTODP( dc, y+sz.cy );
87         }
88         else
89         {
90             rect.left   = XLPTODP( dc, lprect->left );
91             rect.right  = XLPTODP( dc, lprect->right );
92             rect.top    = YLPTODP( dc, lprect->top );
93             rect.bottom = YLPTODP( dc, lprect->bottom );
94         }
95         if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
96         if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
97     }
98
99     x = XLPTODP( dc, x );
100     y = YLPTODP( dc, y );
101
102     TRACE(text,"\treal coord: x=%i, y=%i, rect=(%d,%d - %d,%d)\n",
103                           x, y, rect.left, rect.top, rect.right, rect.bottom);
104
105       /* Draw the rectangle */
106
107     if (flags & ETO_OPAQUE)
108     {
109         TSXSetForeground( display, physDev->gc, physDev->backgroundPixel );
110         TSXFillRectangle( display, physDev->drawable, physDev->gc,
111                         dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
112                         rect.right-rect.left, rect.bottom-rect.top );
113     }
114     if (!count) return TRUE;  /* Nothing more to do */
115
116       /* Compute text starting position */
117
118     if (lpDx) /* have explicit character cell x offsets in logical coordinates */
119     {
120         int extra = dc->wndExtX / 2;
121         for (i = width = 0; i < count; i++) width += lpDx[i];
122         width = (width * dc->vportExtX + extra ) / dc->wndExtX;
123     }
124     else
125     {
126         SIZE32 sz;
127         if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
128             return FALSE;
129         width = XLSTODS(dc, sz.cx);
130     }
131     ascent = pfo->lpX11Trans ? pfo->lpX11Trans->ascent : font->ascent;
132     descent = pfo->lpX11Trans ? pfo->lpX11Trans->descent : font->descent;
133     xwidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->a /
134       pfo->lpX11Trans->pixelsize : width;
135     ywidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->b /
136       pfo->lpX11Trans->pixelsize : 0;
137
138     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
139     {
140       case TA_LEFT:
141           if (dc->w.textAlign & TA_UPDATECP) {
142               dc->w.CursPosX = XDPTOLP( dc, x + xwidth );
143               dc->w.CursPosY = YDPTOLP( dc, y - ywidth );
144           }
145           break;
146       case TA_RIGHT:
147           x -= xwidth;
148           y += ywidth;
149           if (dc->w.textAlign & TA_UPDATECP) {
150               dc->w.CursPosX = XDPTOLP( dc, x );
151               dc->w.CursPosY = YDPTOLP( dc, y );
152           }
153           break;
154       case TA_CENTER:
155           x -= xwidth / 2;
156           y += ywidth / 2;
157           break;
158     }
159
160     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
161     {
162       case TA_TOP:
163           x -= pfo->lpX11Trans ? ascent * pfo->lpX11Trans->c /
164             pfo->lpX11Trans->pixelsize : 0;
165           y += pfo->lpX11Trans ? ascent * pfo->lpX11Trans->d /
166             pfo->lpX11Trans->pixelsize : ascent;
167           break;
168       case TA_BOTTOM:
169           x += pfo->lpX11Trans ? descent * pfo->lpX11Trans->c /
170             pfo->lpX11Trans->pixelsize : 0;
171           y -= pfo->lpX11Trans ? descent * pfo->lpX11Trans->d /
172             pfo->lpX11Trans->pixelsize : descent;
173           break;
174       case TA_BASELINE:
175           break;
176     }
177
178       /* Set the clip region */
179
180     if (flags & ETO_CLIPPED)
181     {
182         SaveVisRgn( dc->hSelf );
183         CLIPPING_IntersectVisRect( dc, rect.left, rect.top, rect.right,
184                                    rect.bottom, FALSE );
185     }
186
187       /* Draw the text background if necessary */
188
189     if (dc->w.backgroundMode != TRANSPARENT)
190     {
191           /* If rectangle is opaque and clipped, do nothing */
192         if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
193         {
194               /* Only draw if rectangle is not opaque or if some */
195               /* text is outside the rectangle */
196             if (!(flags & ETO_OPAQUE) ||
197                 (x < rect.left) ||
198                 (x + width >= rect.right) ||
199                 (y - ascent < rect.top) ||
200                 (y + descent >= rect.bottom))
201             {
202                 TSXSetForeground( display, physDev->gc,
203                                   physDev->backgroundPixel );
204                 TSXFillRectangle( display, physDev->drawable, physDev->gc,
205                                 dc->w.DCOrgX + x,
206                                 dc->w.DCOrgY + y - ascent,
207                                 width,
208                                 ascent + descent );
209             }
210         }
211     }
212     
213     /* Draw the text (count > 0 verified) */
214
215     TSXSetForeground( display, physDev->gc, physDev->textPixel );
216     if(!rotated)
217     {
218       if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
219       {
220         TSXDrawString( display, physDev->drawable, physDev->gc, 
221                      dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
222       }
223       else  /* Now the fun begins... */
224       {
225         XTextItem *items, *pitem;
226         int delta;
227
228         /* allocate max items */
229
230         pitem = items = HEAP_xalloc( GetProcessHeap(), 0,
231                                      count * sizeof(XTextItem) );
232         delta = i = 0;
233         if( lpDx ) /* explicit character widths */
234         {
235             int extra = dc->wndExtX / 2;
236
237             while (i < count)
238             {
239                 /* initialize text item with accumulated delta */
240
241                 pitem->chars  = (char *)str + i;
242                 pitem->delta  = delta;
243                 pitem->nchars = 0;
244                 pitem->font   = None;
245                 delta = 0;
246
247                 /* add characters  to the same XTextItem until new delta
248                  * becomes  non-zero */
249
250                 do
251                 {
252                     delta += (lpDx[i] * dc->vportExtX + extra) / dc->wndExtX
253                                             - TSXTextWidth( font, str + i, 1);
254                     pitem->nchars++;
255                 } while ((++i < count) && !delta);
256                 pitem++;
257            }
258         }
259         else /* charExtra or breakExtra */
260         {
261             while (i < count)
262             {
263                 pitem->chars  = (char *)str + i;
264                 pitem->delta  = delta;
265                 pitem->nchars = 0;
266                 pitem->font   = None;
267                 delta = 0;
268
269                 do
270                 {
271                     delta += dc->w.charExtra;
272                     if (str[i] == (char)dfBreakChar) delta += dc->w.breakExtra;
273                     pitem->nchars++;
274                 } while ((++i < count) && !delta);
275                 pitem++;
276             } 
277         }
278
279         TSXDrawText( display, physDev->drawable, physDev->gc,
280                    dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
281         HeapFree( GetProcessHeap(), 0, items );
282       }
283     }
284     else /* rotated */
285     {  
286       /* have to render character by character. */
287       double offset = 0.0;
288       int i;
289
290       for (i=0; i<count; i++)
291       {
292         int char_metric_offset = (unsigned char) str[i] 
293           - font->min_char_or_byte2;
294         int x_i = IROUND((double) (dc->w.DCOrgX + x) + offset *
295                          pfo->lpX11Trans->a / pfo->lpX11Trans->pixelsize );
296         int y_i = IROUND((double) (dc->w.DCOrgY + y) - offset *
297                          pfo->lpX11Trans->b / pfo->lpX11Trans->pixelsize );
298
299         TSXDrawString( display, physDev->drawable, physDev->gc,
300                        x_i, y_i, &str[i], 1);
301         if (lpDx)
302           offset += XLSTODS(dc, lpDx[i]);
303         else
304         {
305           offset += (double) (font->per_char ?
306                               font->per_char[char_metric_offset].attributes:
307                               font->min_bounds.attributes)
308                           * pfo->lpX11Trans->pixelsize / 1000.0;
309           offset += dc->w.charExtra;
310           if (str[i] == (char)dfBreakChar)
311             offset += dc->w.breakExtra;
312         }
313       }
314     }
315
316       /* Draw underline and strike-out if needed */
317
318     if (lfUnderline)
319     {
320         long linePos, lineWidth;       
321
322         if (!TSXGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
323             linePos = descent - 1;
324         if (!TSXGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
325             lineWidth = 0;
326         else if (lineWidth == 1) lineWidth = 0;
327         TSXSetLineAttributes( display, physDev->gc, lineWidth,
328                               LineSolid, CapRound, JoinBevel ); 
329         TSXDrawLine( display, physDev->drawable, physDev->gc,
330                      dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
331                      dc->w.DCOrgX + x + width, dc->w.DCOrgY + y + linePos );
332     }
333     if (lfStrikeOut)
334     {
335         long lineAscent, lineDescent;
336         if (!TSXGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
337             lineAscent = ascent / 2;
338         if (!TSXGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
339             lineDescent = -lineAscent * 2 / 3;
340         TSXSetLineAttributes( display, physDev->gc, lineAscent + lineDescent,
341                             LineSolid, CapRound, JoinBevel ); 
342         TSXDrawLine( display, physDev->drawable, physDev->gc,
343                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
344                    dc->w.DCOrgX + x + width, dc->w.DCOrgY + y - lineAscent );
345     }
346
347     if (flags & ETO_CLIPPED) 
348         RestoreVisRgn( dc->hSelf );
349
350     return TRUE;
351 }
352
353 #endif /* !defined(X_DISPLAY_MISSING) */