]> Pileus Git - ~andy/gtk/blob - gdk/gdkpango.c
Don't draw runs with a PANGO_ATTR_SHAPE set.
[~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                                            PangoAttrColor *fg_color,
37                                            gboolean       *fg_set,
38                                            PangoAttrColor *bg_color,
39                                            gboolean       *bg_set,
40                                            gboolean       *shape_set);
41
42 static void
43 gdk_pango_context_destroy (GdkPangoContextInfo *info)
44 {
45   gdk_colormap_unref (info->colormap);
46   g_free (info);
47 }
48
49 static GdkPangoContextInfo *
50 gdk_pango_context_get_info (PangoContext *context, gboolean create)
51 {
52   GdkPangoContextInfo *info =
53     g_object_get_qdata (G_OBJECT (context),
54                         g_quark_try_string (GDK_INFO_KEY));
55   if (!info && create)
56     {
57       info = g_new (GdkPangoContextInfo, 1);
58       info->colormap = NULL;
59
60       g_object_set_qdata_full (G_OBJECT (context),
61                                g_quark_from_static_string (GDK_INFO_KEY),
62                                info, (GDestroyNotify)gdk_pango_context_destroy);
63     }
64
65   return info;
66 }
67
68 static GdkGC *
69 gdk_pango_get_gc (PangoContext   *context,
70                   PangoAttrColor *fg_color,
71                   GdkGC          *base_gc)
72 {
73   GdkPangoContextInfo *info;
74   GdkColormap *colormap;
75   GdkColor color;
76   
77   g_return_val_if_fail (context != NULL, NULL);
78
79   info = gdk_pango_context_get_info (context, FALSE);
80
81   if (info && info->colormap)
82     colormap = info->colormap;
83   else
84     colormap = gdk_colormap_get_system();
85
86   /* FIXME. FIXME. FIXME. Only works for true color */
87
88   color.red = fg_color->red;
89   color.green = fg_color->green;
90   color.blue = fg_color->blue;
91   
92   if (gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE))
93     {
94       GdkGC *result = gdk_gc_new (gdk_parent_root);
95       gdk_gc_copy (result, base_gc);
96       gdk_gc_set_foreground (result, &color);
97
98       return result;
99     }
100   else
101     return gdk_gc_ref (base_gc);
102 }
103
104 static void
105 gdk_pango_free_gc (PangoContext *context,
106                    GdkGC        *gc)
107 {
108   gdk_gc_unref (gc);
109 }
110
111 void
112 gdk_pango_context_set_colormap (PangoContext *context,
113                                 GdkColormap  *colormap)
114 {
115   GdkPangoContextInfo *info;
116   
117   g_return_if_fail (context != NULL);
118
119   info = gdk_pango_context_get_info (context, TRUE);
120   g_return_if_fail (info != NULL);
121   
122   if (info->colormap != colormap)
123     {
124       if (info->colormap)
125         gdk_colormap_unref (info->colormap);
126
127       info->colormap = colormap;
128       
129       if (info->colormap)
130         gdk_colormap_ref (info->colormap);
131     }
132 }
133
134
135 /**
136  * gdk_draw_layout_line:
137  * @drawable:  the drawable on which to draw the line
138  * @gc:        base graphics to use
139  * @x:         the x position of start of string (in pixels)
140  * @y:         the y position of baseline (in pixels)
141  * @line:      a #PangoLayoutLine
142  *
143  * Render a #PangoLayoutLine onto an GDK drawable
144  */
145 void 
146 gdk_draw_layout_line (GdkDrawable      *drawable,
147                       GdkGC            *gc,
148                       gint              x, 
149                       gint              y,
150                       PangoLayoutLine  *line)
151 {
152   GSList *tmp_list = line->runs;
153   PangoRectangle overall_rect;
154   PangoRectangle logical_rect;
155   PangoRectangle ink_rect;
156   PangoContext *context;
157   gint x_off = 0;
158
159   g_return_if_fail (drawable != NULL);
160   g_return_if_fail (gc != NULL);
161   g_return_if_fail (line != NULL);
162
163   context = pango_layout_get_context (line->layout);
164   
165   pango_layout_line_get_extents (line,NULL, &overall_rect);
166   
167   while (tmp_list)
168     {
169       PangoUnderline uline = PANGO_UNDERLINE_NONE;
170       PangoLayoutRun *run = tmp_list->data;
171       PangoAttrColor fg_color, bg_color;
172       gboolean fg_set, bg_set, shape_set;
173       GdkGC *fg_gc;
174       
175       tmp_list = tmp_list->next;
176
177       gdk_pango_get_item_properties (run->item, &uline, &fg_color, &fg_set, &bg_color, &bg_set, &shape_set);
178
179       if (uline == PANGO_UNDERLINE_NONE)
180         pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
181                                     NULL, &logical_rect);
182       else
183         pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
184                                     &ink_rect, &logical_rect);
185
186       if (bg_set)
187         {
188           GdkGC *bg_gc = gdk_pango_get_gc (context, &bg_color, gc);
189
190           gdk_draw_rectangle (drawable, bg_gc, TRUE,
191                               x + (x_off + logical_rect.x) / PANGO_SCALE,
192                               y + overall_rect.y / PANGO_SCALE,
193                               logical_rect.width / PANGO_SCALE,
194                               overall_rect.height / PANGO_SCALE);
195
196           gdk_pango_free_gc (context, bg_gc);
197         }
198
199       if (shape_set)
200         continue;
201
202       if (fg_set)
203         fg_gc = gdk_pango_get_gc (context, &fg_color, gc);
204       else
205         fg_gc = gc;
206
207       gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
208                        x + x_off / PANGO_SCALE, y, run->glyphs);
209
210       switch (uline)
211         {
212         case PANGO_UNDERLINE_NONE:
213           break;
214         case PANGO_UNDERLINE_DOUBLE:
215           gdk_draw_line (drawable, fg_gc,
216                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4,
217                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
218           /* Fall through */
219         case PANGO_UNDERLINE_SINGLE:
220           gdk_draw_line (drawable, fg_gc,
221                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
222                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
223           break;
224         case PANGO_UNDERLINE_LOW:
225           gdk_draw_line (drawable, fg_gc,
226                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2,
227                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2);
228           break;
229         }
230
231       if (fg_set)
232         gdk_pango_free_gc (context, fg_gc);
233       
234       x_off += logical_rect.width;
235     }
236 }
237
238 /**
239  * gdk_draw_layout:
240  * @drawable:  the drawable on which to draw string
241  * @gc:        base graphics context to use
242  * @x:         the X position of the left of the layout (in pixels)
243  * @y:         the Y position of the top of the layout (in pixels)
244  * @layout:    a #PangoLayout
245  *
246  * Render a #PangoLayout onto a GDK drawable
247  */
248 void 
249 gdk_draw_layout (GdkDrawable     *drawable,
250                  GdkGC           *gc,
251                  int              x, 
252                  int              y,
253                  PangoLayout     *layout)
254 {
255   PangoRectangle logical_rect;
256   GSList *tmp_list;
257   PangoAlignment align;
258   gint indent;
259   gint width;
260   gint y_offset = 0;
261   gboolean first = FALSE;
262   
263   g_return_if_fail (drawable != NULL);
264   g_return_if_fail (gc != NULL);
265   g_return_if_fail (layout != NULL);
266
267   indent = pango_layout_get_indent (layout);
268   width = pango_layout_get_width (layout);
269   align = pango_layout_get_alignment (layout);
270
271   if (width == -1 && align != PANGO_ALIGN_LEFT)
272     {
273       pango_layout_get_extents (layout, NULL, &logical_rect);
274       width = logical_rect.width;
275     }
276   
277   tmp_list = pango_layout_get_lines (layout);
278   while (tmp_list)
279     {
280       PangoLayoutLine *line = tmp_list->data;
281       int x_offset;
282       
283       pango_layout_line_get_extents (line, NULL, &logical_rect);
284
285       if (width != -1 && align == PANGO_ALIGN_RIGHT)
286         x_offset = width - logical_rect.width;
287       else if (width != -1 && align == PANGO_ALIGN_CENTER)
288         x_offset = (width - logical_rect.width) / 2;
289       else
290         x_offset = 0;
291
292       if (first)
293         {
294           if (indent > 0)
295             {
296               if (align == PANGO_ALIGN_LEFT)
297                 x_offset += indent;
298               else
299                 x_offset -= indent;
300             }
301
302           first = FALSE;
303         }
304       else
305         {
306           if (indent < 0)
307             {
308               if (align == PANGO_ALIGN_LEFT)
309                 x_offset -= indent;
310               else
311                 x_offset += indent;
312             }
313         }
314           
315       gdk_draw_layout_line (drawable, gc,
316                             x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
317                             line);
318
319       y_offset += logical_rect.height;
320       tmp_list = tmp_list->next;
321     }
322 }
323
324 static void
325 gdk_pango_get_item_properties (PangoItem      *item,
326                                PangoUnderline *uline,
327                                PangoAttrColor *fg_color,
328                                gboolean       *fg_set,
329                                PangoAttrColor *bg_color,
330                                gboolean       *bg_set,
331                                gboolean       *shape_set)
332 {
333   GSList *tmp_list = item->extra_attrs;
334
335   if (fg_set)
336     *fg_set = FALSE;
337   
338   if (bg_set)
339     *bg_set = FALSE;
340
341   if (shape_set)
342     *shape_set = FALSE;
343   
344   while (tmp_list)
345     {
346       PangoAttribute *attr = tmp_list->data;
347
348       switch (attr->klass->type)
349         {
350         case PANGO_ATTR_UNDERLINE:
351           if (uline)
352             *uline = ((PangoAttrInt *)attr)->value;
353           break;
354           
355         case PANGO_ATTR_FOREGROUND:
356           if (fg_color)
357             *fg_color = *((PangoAttrColor *)attr);
358           if (fg_set)
359             *fg_set = TRUE;
360           
361           break;
362           
363         case PANGO_ATTR_BACKGROUND:
364           if (bg_color)
365             *bg_color = *((PangoAttrColor *)attr);
366           if (bg_set)
367             *bg_set = TRUE;
368           
369           break;
370
371         case PANGO_ATTR_SHAPE:
372           if (shape_set)
373             *shape_set = TRUE;
374           break;
375           
376         default:
377           break;
378         }
379       tmp_list = tmp_list->next;
380     }
381 }
382