Release 971116
[wine] / graphics / x11drv / text.c
1 /*
2  * X11 graphics driver text functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <X11/Xlib.h>
9 #include <X11/Xatom.h>
10 #include "windows.h"
11 #include "dc.h"
12 #include "gdi.h"
13 /*#include "callback.h"*/
14 #include "heap.h"
15 #include "x11font.h"
16 #include "stddebug.h"
17 /* #define DEBUG_TEXT */
18 #include "debug.h"
19
20 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
21
22
23 /***********************************************************************
24  *           X11DRV_ExtTextOut
25  */
26 BOOL32
27 X11DRV_ExtTextOut( DC *dc, INT32 x, INT32 y, UINT32 flags,
28                    const RECT32 *lprect, LPCSTR str, UINT32 count,
29                    const INT32 *lpDx )
30 {
31     HRGN32              hRgnClip = 0;
32     int                 dir, ascent, descent, i;
33     fontObject*         pfo;
34     XCharStruct         info;
35     XFontStruct*        font;
36     RECT32              rect;
37     char                dfBreakChar, lfUnderline, lfStrikeOut;
38
39     if (!DC_SetupGCForText( dc )) return TRUE;
40
41     pfo = XFONT_GetFontObject( dc->u.x.font );
42     font = pfo->fs;
43
44     dfBreakChar = (char)pfo->fi->df.dfBreakChar;
45     lfUnderline = (pfo->fo_flags & FO_SYNTH_UNDERLINE) ? 1 : 0;
46     lfStrikeOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT) ? 1 : 0;
47
48     dprintf_text(stddeb,"ExtTextOut: hdc=%04x df=%04x %d,%d '%.*s', %d  flags=%d\n",
49                  dc->hSelf, (UINT16)(dc->u.x.font), x, y, (int)count, str, count, flags);
50
51     if (lprect != NULL) dprintf_text(stddeb, "\trect=(%d,%d- %d,%d)\n",
52                                      lprect->left, lprect->top,
53                                      lprect->right, lprect->bottom );
54       /* Setup coordinates */
55
56     if (dc->w.textAlign & TA_UPDATECP)
57     {
58         x = dc->w.CursPosX;
59         y = dc->w.CursPosY;
60     }
61
62     if (flags & (ETO_OPAQUE | ETO_CLIPPED))  /* there's a rectangle */
63     {
64         if (!lprect)  /* not always */
65         {
66             SIZE32 sz;
67             if (flags & ETO_CLIPPED)  /* Can't clip with no rectangle */
68               return FALSE;
69             if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
70               return FALSE;
71             rect.left   = XLPTODP( dc, x );
72             rect.right  = XLPTODP( dc, x+sz.cx );
73             rect.top    = YLPTODP( dc, y );
74             rect.bottom = YLPTODP( dc, y+sz.cy );
75         }
76         else
77         {
78             rect.left   = XLPTODP( dc, lprect->left );
79             rect.right  = XLPTODP( dc, lprect->right );
80             rect.top    = YLPTODP( dc, lprect->top );
81             rect.bottom = YLPTODP( dc, lprect->bottom );
82         }
83         if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
84         if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
85     }
86
87     x = XLPTODP( dc, x );
88     y = YLPTODP( dc, y );
89
90     dprintf_text(stddeb,"\treal coord: x=%i, y=%i, rect=(%d,%d-%d,%d)\n",
91                           x, y, rect.left, rect.top, rect.right, rect.bottom);
92
93       /* Draw the rectangle */
94
95     if (flags & ETO_OPAQUE)
96     {
97         XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
98         XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
99                         dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
100                         rect.right-rect.left, rect.bottom-rect.top );
101     }
102     if (!count) return TRUE;  /* Nothing more to do */
103
104       /* Compute text starting position */
105
106     if (lpDx) /* have explicit character cell x offsets in logical coordinates */
107     {
108         int extra = dc->wndExtX / 2;
109         for (i = info.width = 0; i < count; i++) info.width += lpDx[i];
110         info.width = (info.width * dc->vportExtX + extra ) / dc->wndExtX;
111     }
112     else
113     {
114         XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
115         info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
116     }
117
118     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
119     {
120       case TA_LEFT:
121           if (dc->w.textAlign & TA_UPDATECP)
122               dc->w.CursPosX = XDPTOLP( dc, x + info.width );
123           break;
124       case TA_RIGHT:
125           x -= info.width;
126           if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
127           break;
128       case TA_CENTER:
129           x -= info.width / 2;
130           break;
131     }
132
133     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
134     {
135       case TA_TOP:
136           y += font->ascent;
137           break;
138       case TA_BOTTOM:
139           y -= font->descent;
140           break;
141       case TA_BASELINE:
142           break;
143     }
144
145       /* Set the clip region */
146
147     if (flags & ETO_CLIPPED)
148     {
149         hRgnClip = dc->w.hClipRgn;
150         CLIPPING_IntersectClipRect( dc, rect.left, rect.top, rect.right,
151                                     rect.bottom, CLIP_INTERSECT|CLIP_KEEPRGN );
152     }
153
154       /* Draw the text background if necessary */
155
156     if (dc->w.backgroundMode != TRANSPARENT)
157     {
158           /* If rectangle is opaque and clipped, do nothing */
159         if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
160         {
161               /* Only draw if rectangle is not opaque or if some */
162               /* text is outside the rectangle */
163             if (!(flags & ETO_OPAQUE) ||
164                 (x < rect.left) ||
165                 (x + info.width >= rect.right) ||
166                 (y-font->ascent < rect.top) ||
167                 (y+font->descent >= rect.bottom))
168             {
169                 XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
170                 XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
171                                 dc->w.DCOrgX + x,
172                                 dc->w.DCOrgY + y - font->ascent,
173                                 info.width,
174                                 font->ascent + font->descent );
175             }
176         }
177     }
178     
179     /* Draw the text (count > 0 verified) */
180
181     XSetForeground( display, dc->u.x.gc, dc->w.textPixel );
182     if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
183     {
184         XDrawString( display, dc->u.x.drawable, dc->u.x.gc, 
185                      dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
186     }
187     else  /* Now the fun begins... */
188     {
189         XTextItem *items, *pitem;
190         int delta;
191
192         /* allocate max items */
193
194         pitem = items = HEAP_xalloc( GetProcessHeap(), 0,
195                                      count * sizeof(XTextItem) );
196         delta = i = 0;
197         if( lpDx ) /* explicit character widths */
198         {
199             int extra = dc->wndExtX / 2;
200
201             while (i < count)
202             {
203                 /* initialize text item with accumulated delta */
204
205                 pitem->chars  = (char *)str + i;
206                 pitem->delta  = delta;
207                 pitem->nchars = 0;
208                 pitem->font   = None;
209                 delta = 0;
210
211                 /* add characters  to the same XTextItem until new delta
212                  * becomes  non-zero */
213
214                 do
215                 {
216                     delta += (lpDx[i] * dc->vportExtX + extra) / dc->wndExtX
217                                             - XTextWidth( font, str + i, 1);
218                     pitem->nchars++;
219                 } while ((++i < count) && !delta);
220                 pitem++;
221            }
222         }
223         else /* charExtra or breakExtra */
224         {
225             while (i < count)
226             {
227                 pitem->chars  = (char *)str + i;
228                 pitem->delta  = delta;
229                 pitem->nchars = 0;
230                 pitem->font   = None;
231                 delta = 0;
232
233                 do
234                 {
235                     delta += dc->w.charExtra;
236                     if (str[i] == (char)dfBreakChar) delta += dc->w.breakExtra;
237                     pitem->nchars++;
238                 } while ((++i < count) && !delta);
239                 pitem++;
240             } 
241         }
242
243         XDrawText( display, dc->u.x.drawable, dc->u.x.gc,
244                    dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
245         HeapFree( GetProcessHeap(), 0, items );
246     }
247
248       /* Draw underline and strike-out if needed */
249
250     if (lfUnderline)
251     {
252         long linePos, lineWidth;       
253
254         if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
255             linePos = font->descent-1;
256         if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
257             lineWidth = 0;
258         else if (lineWidth == 1) lineWidth = 0;
259         XSetLineAttributes( display, dc->u.x.gc, lineWidth,
260                             LineSolid, CapRound, JoinBevel ); 
261         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
262                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
263                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
264     }
265     if (lfStrikeOut)
266     {
267         long lineAscent, lineDescent;
268         if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
269             lineAscent = font->ascent / 2;
270         if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
271             lineDescent = -lineAscent * 2 / 3;
272         XSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
273                             LineSolid, CapRound, JoinBevel ); 
274         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
275                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
276                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
277     }
278
279     if (flags & ETO_CLIPPED) 
280     {
281       SelectClipRgn32( dc->hSelf, hRgnClip );
282       DeleteObject32( hRgnClip );
283     }
284     return TRUE;
285 }