]> Pileus Git - ~andy/gtk/blob - gdk/gdkpango.c
Draw underlines one pixel higher.
[~andy/gtk] / gdk / gdkpango.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2000 Red Hat, Inc. 
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "gdkcolor.h"
21 #include "gdkgc.h"
22 #include "gdkpango.h"
23 #include "gdkprivate.h"
24
25 #define GDK_INFO_KEY "gdk-info"
26
27 typedef struct _GdkPangoContextInfo GdkPangoContextInfo;
28
29 struct _GdkPangoContextInfo
30 {
31   GdkColormap *colormap;
32 };
33
34 static void gdk_pango_get_item_properties (PangoItem      *item,
35                                            PangoUnderline *uline,
36                                            gint           *rise,
37                                            PangoAttrColor *fg_color,
38                                            gboolean       *fg_set,
39                                            PangoAttrColor *bg_color,
40                                            gboolean       *bg_set,
41                                            gboolean       *shape_set,
42                                            PangoRectangle *ink_rect,
43                                            PangoRectangle *logical_rect);
44
45 static void
46 gdk_pango_context_destroy (GdkPangoContextInfo *info)
47 {
48   gdk_colormap_unref (info->colormap);
49   g_free (info);
50 }
51
52 static GdkPangoContextInfo *
53 gdk_pango_context_get_info (PangoContext *context, gboolean create)
54 {
55   GdkPangoContextInfo *info =
56     g_object_get_qdata (G_OBJECT (context),
57                         g_quark_try_string (GDK_INFO_KEY));
58   if (!info && create)
59     {
60       info = g_new (GdkPangoContextInfo, 1);
61       info->colormap = NULL;
62
63       g_object_set_qdata_full (G_OBJECT (context),
64                                g_quark_from_static_string (GDK_INFO_KEY),
65                                info, (GDestroyNotify)gdk_pango_context_destroy);
66     }
67
68   return info;
69 }
70
71 static GdkGC *
72 gdk_pango_get_gc (PangoContext   *context,
73                   PangoAttrColor *fg_color,
74                   GdkGC          *base_gc)
75 {
76   GdkPangoContextInfo *info;
77   GdkColormap *colormap;
78   GdkColor color;
79   
80   g_return_val_if_fail (context != NULL, NULL);
81
82   info = gdk_pango_context_get_info (context, FALSE);
83
84   if (info && info->colormap)
85     colormap = info->colormap;
86   else
87     colormap = gdk_colormap_get_system();
88
89   /* FIXME. FIXME. FIXME. Only works for true color */
90
91   color.red = fg_color->red;
92   color.green = fg_color->green;
93   color.blue = fg_color->blue;
94   
95   if (gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE))
96     {
97       GdkGC *result = gdk_gc_new (gdk_parent_root);
98       gdk_gc_copy (result, base_gc);
99       gdk_gc_set_foreground (result, &color);
100
101       return result;
102     }
103   else
104     return gdk_gc_ref (base_gc);
105 }
106
107 static void
108 gdk_pango_free_gc (PangoContext *context,
109                    GdkGC        *gc)
110 {
111   gdk_gc_unref (gc);
112 }
113
114 void
115 gdk_pango_context_set_colormap (PangoContext *context,
116                                 GdkColormap  *colormap)
117 {
118   GdkPangoContextInfo *info;
119   
120   g_return_if_fail (context != NULL);
121
122   info = gdk_pango_context_get_info (context, TRUE);
123   g_return_if_fail (info != NULL);
124   
125   if (info->colormap != colormap)
126     {
127       if (info->colormap)
128         gdk_colormap_unref (info->colormap);
129
130       info->colormap = colormap;
131       
132       if (info->colormap)
133         gdk_colormap_ref (info->colormap);
134     }
135 }
136
137 /**
138  * gdk_draw_layout_line:
139  * @drawable:  the drawable on which to draw the line
140  * @gc:        base graphics to use
141  * @x:         the x position of start of string (in pixels)
142  * @y:         the y position of baseline (in pixels)
143  * @line:      a #PangoLayoutLine
144  *
145  * Render a #PangoLayoutLine onto an GDK drawable
146  */
147 void 
148 gdk_draw_layout_line (GdkDrawable      *drawable,
149                       GdkGC            *gc,
150                       gint              x, 
151                       gint              y,
152                       PangoLayoutLine  *line)
153 {
154   GSList *tmp_list = line->runs;
155   PangoRectangle overall_rect;
156   PangoRectangle logical_rect;
157   PangoRectangle ink_rect;
158   PangoContext *context;
159   gint x_off = 0;
160   gint rise = 0;
161   
162   g_return_if_fail (drawable != NULL);
163   g_return_if_fail (gc != NULL);
164   g_return_if_fail (line != NULL);
165
166   context = pango_layout_get_context (line->layout);
167   
168   pango_layout_line_get_extents (line,NULL, &overall_rect);
169   
170   while (tmp_list)
171     {
172       PangoUnderline uline = PANGO_UNDERLINE_NONE;
173       PangoLayoutRun *run = tmp_list->data;
174       PangoAttrColor fg_color, bg_color;
175       gboolean fg_set, bg_set, shape_set;
176       GdkGC *fg_gc;
177       gint risen_y;
178       
179       tmp_list = tmp_list->next;
180       
181       gdk_pango_get_item_properties (run->item, &uline,
182                                      &rise,
183                                      &fg_color, &fg_set,
184                                      &bg_color, &bg_set,
185                                      &shape_set,
186                                      &ink_rect,
187                                      &logical_rect);
188
189       /* we subtract the rise because X coordinates are upside down */
190       risen_y = y - rise / PANGO_SCALE;
191       
192       if (!shape_set)
193         {
194           if (uline == PANGO_UNDERLINE_NONE)
195             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
196                                         NULL, &logical_rect);
197           else
198             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
199                                         &ink_rect, &logical_rect);
200         }
201
202       if (bg_set)
203         {
204           GdkGC *bg_gc = gdk_pango_get_gc (context, &bg_color, gc);
205
206           gdk_draw_rectangle (drawable, bg_gc, TRUE,
207                               x + (x_off + logical_rect.x) / PANGO_SCALE,
208                               risen_y + overall_rect.y / PANGO_SCALE,
209                               logical_rect.width / PANGO_SCALE,
210                               overall_rect.height / PANGO_SCALE);
211
212           gdk_pango_free_gc (context, bg_gc);
213         }
214
215       if (fg_set)
216         fg_gc = gdk_pango_get_gc (context, &fg_color, gc);
217       else
218         fg_gc = gc;
219
220       if (!shape_set)
221         gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
222                          x + x_off / PANGO_SCALE,
223                          risen_y,
224                          run->glyphs);
225
226       switch (uline)
227         {
228         case PANGO_UNDERLINE_NONE:
229           break;
230         case PANGO_UNDERLINE_DOUBLE:
231           gdk_draw_line (drawable, fg_gc,
232                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
233                          risen_y + 3,
234                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
235                          risen_y + 3);
236           /* Fall through */
237         case PANGO_UNDERLINE_SINGLE:
238           gdk_draw_line (drawable, fg_gc,
239                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
240                          risen_y + 1,
241                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
242                          risen_y + 1);
243           break;
244         case PANGO_UNDERLINE_LOW:
245           gdk_draw_line (drawable, fg_gc,
246                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
247                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
248                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
249                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
250           break;
251         }
252
253       if (fg_set)
254         gdk_pango_free_gc (context, fg_gc);
255
256       x_off += logical_rect.width;
257     }
258 }
259
260 /**
261  * gdk_draw_layout:
262  * @drawable:  the drawable on which to draw string
263  * @gc:        base graphics context to use
264  * @x:         the X position of the left of the layout (in pixels)
265  * @y:         the Y position of the top of the layout (in pixels)
266  * @layout:    a #PangoLayout
267  *
268  * Render a #PangoLayout onto a GDK drawable
269  */
270 void 
271 gdk_draw_layout (GdkDrawable     *drawable,
272                  GdkGC           *gc,
273                  int              x, 
274                  int              y,
275                  PangoLayout     *layout)
276 {
277   PangoLayoutIter *iter;
278   
279   g_return_if_fail (drawable != NULL);
280   g_return_if_fail (gc != NULL);
281   g_return_if_fail (layout != NULL);
282
283   iter = pango_layout_get_iter (layout);
284   
285   do
286     {
287       PangoRectangle logical_rect;
288       PangoLayoutLine *line;
289       int baseline;
290       
291       line = pango_layout_iter_get_line (iter);
292       
293       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
294       baseline = pango_layout_iter_get_baseline (iter);
295       
296       gdk_draw_layout_line (drawable, gc,
297                             x + logical_rect.x / PANGO_SCALE,
298                             y + baseline / PANGO_SCALE,
299                             line);
300     }
301   while (pango_layout_iter_next_line (iter));
302
303   pango_layout_iter_free (iter);
304 }
305
306 static void
307 gdk_pango_get_item_properties (PangoItem      *item,
308                                PangoUnderline *uline,
309                                gint           *rise,
310                                PangoAttrColor *fg_color,
311                                gboolean       *fg_set,
312                                PangoAttrColor *bg_color,
313                                gboolean       *bg_set,
314                                gboolean       *shape_set,
315                                PangoRectangle *ink_rect,
316                                PangoRectangle *logical_rect)
317 {
318   GSList *tmp_list = item->extra_attrs;
319
320   if (fg_set)
321     *fg_set = FALSE;
322   
323   if (bg_set)
324     *bg_set = FALSE;
325
326   if (shape_set)
327     *shape_set = FALSE;
328
329   if (rise)
330     *rise = 0;
331   
332   while (tmp_list)
333     {
334       PangoAttribute *attr = tmp_list->data;
335
336       switch (attr->klass->type)
337         {
338         case PANGO_ATTR_UNDERLINE:
339           if (uline)
340             *uline = ((PangoAttrInt *)attr)->value;
341           break;
342           
343         case PANGO_ATTR_FOREGROUND:
344           if (fg_color)
345             *fg_color = *((PangoAttrColor *)attr);
346           if (fg_set)
347             *fg_set = TRUE;
348           
349           break;
350           
351         case PANGO_ATTR_BACKGROUND:
352           if (bg_color)
353             *bg_color = *((PangoAttrColor *)attr);
354           if (bg_set)
355             *bg_set = TRUE;
356           
357           break;
358
359         case PANGO_ATTR_SHAPE:
360           if (shape_set)
361             *shape_set = TRUE;
362           if (logical_rect)
363             *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
364           if (ink_rect)
365             *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
366           break;
367
368         case PANGO_ATTR_RISE:
369           if (rise)
370             *rise = ((PangoAttrInt *)attr)->value;
371           break;
372           
373         default:
374           break;
375         }
376       tmp_list = tmp_list->next;
377     }
378 }
379