]> Pileus Git - ~andy/gtk/blob - gdk/gdkpango.c
adapt to handle PangoColor
[~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 PangoAttrType gdk_pango_attr_stipple_type;
35 static PangoAttrType gdk_pango_attr_embossed_type;
36
37 static void gdk_pango_get_item_properties (PangoItem      *item,
38                                            PangoUnderline *uline,
39                                            gint           *rise,
40                                            PangoColor     *fg_color,
41                                            gboolean       *fg_set,
42                                            PangoColor     *bg_color,
43                                            gboolean       *bg_set,
44                                            gboolean       *embossed,
45                                            GdkBitmap     **stipple,
46                                            gboolean       *shape_set,
47                                            PangoRectangle *ink_rect,
48                                            PangoRectangle *logical_rect);
49
50 static void
51 gdk_pango_context_destroy (GdkPangoContextInfo *info)
52 {
53   gdk_colormap_unref (info->colormap);
54   g_free (info);
55 }
56
57 static GdkPangoContextInfo *
58 gdk_pango_context_get_info (PangoContext *context, gboolean create)
59 {
60   GdkPangoContextInfo *info =
61     g_object_get_qdata (G_OBJECT (context),
62                         g_quark_try_string (GDK_INFO_KEY));
63   if (!info && create)
64     {
65       info = g_new (GdkPangoContextInfo, 1);
66       info->colormap = NULL;
67
68       g_object_set_qdata_full (G_OBJECT (context),
69                                g_quark_from_static_string (GDK_INFO_KEY),
70                                info, (GDestroyNotify)gdk_pango_context_destroy);
71     }
72
73   return info;
74 }
75
76 static GdkGC *
77 gdk_pango_get_gc (PangoContext   *context,
78                   PangoColor     *fg_color,
79                   GdkBitmap      *stipple,
80                   GdkGC          *base_gc)
81 {
82   GdkColor color;
83   GdkGC *result;
84   GdkPangoContextInfo *info;
85   
86   g_return_val_if_fail (context != NULL, NULL);
87
88   info = gdk_pango_context_get_info (context, FALSE);
89
90   if (info == NULL || info->colormap == NULL)
91     {
92       g_warning ("you must set the colormap on a PangoContext before using it to draw a layout");
93       return NULL;
94     }
95   
96   color.red = fg_color->red;
97   color.green = fg_color->green;
98   color.blue = fg_color->blue;
99   
100   result = gdk_gc_new (gdk_parent_root);
101   gdk_gc_copy (result, base_gc);
102   gdk_rgb_find_color (info->colormap, &color);
103   gdk_gc_set_foreground (result, &color);
104
105   if (stipple)
106     {
107       gdk_gc_set_fill (result, GDK_STIPPLED);
108       gdk_gc_set_stipple (result, stipple);
109     }
110   
111   return result;
112 }
113
114 static void
115 gdk_pango_free_gc (PangoContext *context,
116                    GdkGC        *gc)
117 {
118   gdk_gc_unref (gc);
119 }
120
121 void
122 gdk_pango_context_set_colormap (PangoContext *context,
123                                 GdkColormap  *colormap)
124 {
125   GdkPangoContextInfo *info;
126   
127   g_return_if_fail (context != NULL);
128
129   info = gdk_pango_context_get_info (context, TRUE);
130   g_return_if_fail (info != NULL);
131   
132   if (info->colormap != colormap)
133     {
134       if (info->colormap)
135         gdk_colormap_unref (info->colormap);
136
137       info->colormap = colormap;
138       
139       if (info->colormap)
140         gdk_colormap_ref (info->colormap);
141     }
142 }
143
144 /**
145  * gdk_draw_layout_line:
146  * @drawable:  the drawable on which to draw the line
147  * @gc:        base graphics to use
148  * @x:         the x position of start of string (in pixels)
149  * @y:         the y position of baseline (in pixels)
150  * @line:      a #PangoLayoutLine
151  *
152  * Render a #PangoLayoutLine onto an GDK drawable
153  */
154 void 
155 gdk_draw_layout_line (GdkDrawable      *drawable,
156                       GdkGC            *gc,
157                       gint              x, 
158                       gint              y,
159                       PangoLayoutLine  *line)
160 {
161   GSList *tmp_list = line->runs;
162   PangoRectangle overall_rect;
163   PangoRectangle logical_rect;
164   PangoRectangle ink_rect;
165   PangoContext *context;
166   gint x_off = 0;
167   gint rise = 0;
168   gboolean embossed;
169   GdkBitmap *stipple;
170   
171   g_return_if_fail (drawable != NULL);
172   g_return_if_fail (gc != NULL);
173   g_return_if_fail (line != NULL);
174
175   context = pango_layout_get_context (line->layout);
176   
177   pango_layout_line_get_extents (line,NULL, &overall_rect);
178   
179   while (tmp_list)
180     {
181       PangoUnderline uline = PANGO_UNDERLINE_NONE;
182       PangoLayoutRun *run = tmp_list->data;
183       PangoColor fg_color, bg_color;
184       gboolean fg_set, bg_set, shape_set;
185       GdkGC *fg_gc;
186       gint risen_y;
187       
188       tmp_list = tmp_list->next;
189       
190       gdk_pango_get_item_properties (run->item, &uline,
191                                      &rise,
192                                      &fg_color, &fg_set,
193                                      &bg_color, &bg_set,
194                                      &embossed,
195                                      &stipple,
196                                      &shape_set,
197                                      &ink_rect,
198                                      &logical_rect);
199
200       /* we subtract the rise because X coordinates are upside down */
201       risen_y = y - rise / PANGO_SCALE;
202       
203       if (!shape_set)
204         {
205           if (uline == PANGO_UNDERLINE_NONE)
206             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
207                                         NULL, &logical_rect);
208           else
209             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
210                                         &ink_rect, &logical_rect);
211         }
212
213       if (bg_set)
214         {
215           GdkGC *bg_gc = gdk_pango_get_gc (context, &bg_color, stipple, gc);
216           
217           gdk_draw_rectangle (drawable, bg_gc, TRUE,
218                               x + (x_off + logical_rect.x) / PANGO_SCALE,
219                               risen_y + overall_rect.y / PANGO_SCALE,
220                               logical_rect.width / PANGO_SCALE,
221                               overall_rect.height / PANGO_SCALE);
222
223           if (stipple)
224             gdk_gc_set_fill (bg_gc, GDK_SOLID);
225           
226           gdk_pango_free_gc (context, bg_gc);
227         }
228
229       if (fg_set || stipple)
230         fg_gc = gdk_pango_get_gc (context, &fg_color, stipple, gc);
231       else
232         fg_gc = gc;
233       
234       if (!shape_set)
235         {
236           gint gx, gy;
237
238           gx = x + x_off / PANGO_SCALE;
239           gy = risen_y;
240           
241           if (embossed)
242             {
243               PangoColor color = { 65535, 65535, 65535 };
244               GdkGC *white_gc = gdk_pango_get_gc (context, &color, stipple, fg_gc);
245               gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font,
246                                gx + 1,
247                                gy + 1,
248                                run->glyphs);
249               gdk_pango_free_gc (context, white_gc);
250             }
251           
252           gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
253                            gx, gy,
254                            run->glyphs);
255         }
256       
257       switch (uline)
258         {
259         case PANGO_UNDERLINE_NONE:
260           break;
261         case PANGO_UNDERLINE_DOUBLE:
262           gdk_draw_line (drawable, fg_gc,
263                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
264                          risen_y + 3,
265                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
266                          risen_y + 3);
267           /* Fall through */
268         case PANGO_UNDERLINE_SINGLE:
269           gdk_draw_line (drawable, fg_gc,
270                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
271                          risen_y + 1,
272                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
273                          risen_y + 1);
274           break;
275         case PANGO_UNDERLINE_LOW:
276           gdk_draw_line (drawable, fg_gc,
277                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
278                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
279                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
280                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
281           break;
282         }
283       
284       if (fg_gc != gc)
285         gdk_pango_free_gc (context, fg_gc);
286
287       x_off += logical_rect.width;
288     }
289 }
290
291 /**
292  * gdk_draw_layout:
293  * @drawable:  the drawable on which to draw string
294  * @gc:        base graphics context to use
295  * @x:         the X position of the left of the layout (in pixels)
296  * @y:         the Y position of the top of the layout (in pixels)
297  * @layout:    a #PangoLayout
298  *
299  * Render a #PangoLayout onto a GDK drawable
300  */
301 void 
302 gdk_draw_layout (GdkDrawable     *drawable,
303                  GdkGC           *gc,
304                  int              x, 
305                  int              y,
306                  PangoLayout     *layout)
307 {
308   PangoLayoutIter *iter;
309   
310   g_return_if_fail (drawable != NULL);
311   g_return_if_fail (gc != NULL);
312   g_return_if_fail (layout != NULL);
313
314   iter = pango_layout_get_iter (layout);
315   
316   do
317     {
318       PangoRectangle logical_rect;
319       PangoLayoutLine *line;
320       int baseline;
321       
322       line = pango_layout_iter_get_line (iter);
323       
324       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
325       baseline = pango_layout_iter_get_baseline (iter);
326       
327       gdk_draw_layout_line (drawable, gc,
328                             x + logical_rect.x / PANGO_SCALE,
329                             y + baseline / PANGO_SCALE,
330                             line);
331     }
332   while (pango_layout_iter_next_line (iter));
333
334   pango_layout_iter_free (iter);
335 }
336
337 static void
338 gdk_pango_get_item_properties (PangoItem      *item,
339                                PangoUnderline *uline,
340                                gint           *rise,
341                                PangoColor     *fg_color,
342                                gboolean       *fg_set,
343                                PangoColor     *bg_color,
344                                gboolean       *bg_set,
345                                gboolean       *embossed,
346                                GdkBitmap     **stipple,
347                                gboolean       *shape_set,
348                                PangoRectangle *ink_rect,
349                                PangoRectangle *logical_rect)
350 {
351   GSList *tmp_list = item->extra_attrs;
352
353   if (fg_set)
354     *fg_set = FALSE;
355   
356   if (bg_set)
357     *bg_set = FALSE;
358
359   if (shape_set)
360     *shape_set = FALSE;
361
362   if (rise)
363     *rise = 0;
364
365   if (embossed)
366     *embossed = FALSE;
367
368   if (stipple)
369     *stipple = NULL;
370   
371   while (tmp_list)
372     {
373       PangoAttribute *attr = tmp_list->data;
374
375       switch (attr->klass->type)
376         {
377         case PANGO_ATTR_UNDERLINE:
378           if (uline)
379             *uline = ((PangoAttrInt *)attr)->value;
380           break;
381           
382         case PANGO_ATTR_FOREGROUND:
383           if (fg_color)
384             *fg_color = ((PangoAttrColor *)attr)->color;
385           if (fg_set)
386             *fg_set = TRUE;
387           
388           break;
389           
390         case PANGO_ATTR_BACKGROUND:
391           if (bg_color)
392             *bg_color = ((PangoAttrColor *)attr)->color;
393           if (bg_set)
394             *bg_set = TRUE;
395           
396           break;
397
398         case PANGO_ATTR_SHAPE:
399           if (shape_set)
400             *shape_set = TRUE;
401           if (logical_rect)
402             *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
403           if (ink_rect)
404             *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
405           break;
406
407         case PANGO_ATTR_RISE:
408           if (rise)
409             *rise = ((PangoAttrInt *)attr)->value;
410           break;
411           
412         default:
413           /* stipple_type and embossed_type aren't necessarily
414            * initialized, but they are 0, which is an
415            * invalid type so won't occur. 
416            */
417           if (stipple && attr->klass->type == gdk_pango_attr_stipple_type)
418             {
419               *stipple = ((GdkPangoAttrStipple*)attr)->stipple;
420             }
421           else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type)
422             {
423               *embossed = ((GdkPangoAttrEmbossed*)attr);
424             }
425           break;
426         }
427       tmp_list = tmp_list->next;
428     }
429 }
430
431
432 static PangoAttribute *
433 gdk_pango_attr_stipple_copy (const PangoAttribute *attr)
434 {
435   const GdkPangoAttrStipple *src = (const GdkPangoAttrStipple*) attr;
436
437   return gdk_pango_attr_stipple_new (src->stipple);
438 }
439
440 static void
441 gdk_pango_attr_stipple_destroy (PangoAttribute *attr)
442 {
443   GdkPangoAttrStipple *st = (GdkPangoAttrStipple*) attr;
444
445   if (st->stipple)
446     g_object_unref (G_OBJECT (st->stipple));
447   
448   g_free (attr);
449 }
450
451 static gboolean
452 gdk_pango_attr_stipple_compare (const PangoAttribute *attr1,
453                                     const PangoAttribute *attr2)
454 {
455   const GdkPangoAttrStipple *a = (const GdkPangoAttrStipple*) attr1;
456   const GdkPangoAttrStipple *b = (const GdkPangoAttrStipple*) attr2;
457
458   return a->stipple == b->stipple;
459 }
460
461 /**
462  * gdk_pango_attr_stipple_new:
463  * @stipple: a bitmap to be set as stipple
464  *
465  * Creates a new attribute containing a stipple bitmap to be used when
466  * rendering the text.
467  *
468  * Return value: new #PangoAttribute
469  **/
470
471 PangoAttribute *
472 gdk_pango_attr_stipple_new (GdkBitmap *stipple)
473 {
474   GdkPangoAttrStipple *result;
475   
476   static PangoAttrClass klass = {
477     0,
478     gdk_pango_attr_stipple_copy,
479     gdk_pango_attr_stipple_destroy,
480     gdk_pango_attr_stipple_compare
481   };
482
483   if (!klass.type)
484     klass.type = gdk_pango_attr_stipple_type =
485       pango_attr_type_register ("GdkPangoAttrStipple");
486
487   result = g_new (GdkPangoAttrStipple, 1);
488   result->attr.klass = &klass;
489
490   if (stipple)
491     g_object_ref (stipple);
492   
493   result->stipple = stipple;
494
495   return (PangoAttribute *)result;
496 }
497
498 static PangoAttribute *
499 gdk_pango_attr_embossed_copy (const PangoAttribute *attr)
500 {
501   const GdkPangoAttrEmbossed *e = (const GdkPangoAttrEmbossed*) attr;
502
503   return gdk_pango_attr_embossed_new (e->embossed);
504 }
505
506 static void
507 gdk_pango_attr_embossed_destroy (PangoAttribute *attr)
508 {
509   g_free (attr);
510 }
511
512 static gboolean
513 gdk_pango_attr_embossed_compare (const PangoAttribute *attr1,
514                                  const PangoAttribute *attr2)
515 {
516   const GdkPangoAttrEmbossed *e1 = (const GdkPangoAttrEmbossed*) attr1;
517   const GdkPangoAttrEmbossed *e2 = (const GdkPangoAttrEmbossed*) attr2;
518
519   return e1->embossed == e2->embossed;
520 }
521
522 /**
523  * gdk_pango_attr_embossed_new:
524  * @embossed: a bitmap to be set as embossed
525  *
526  * Creates a new attribute containing a embossed bitmap to be used when
527  * rendering the text.
528  *
529  * Return value: new #PangoAttribute
530  **/
531
532 PangoAttribute *
533 gdk_pango_attr_embossed_new (gboolean embossed)
534 {
535   GdkPangoAttrEmbossed *result;
536   
537   static PangoAttrClass klass = {
538     0,
539     gdk_pango_attr_embossed_copy,
540     gdk_pango_attr_embossed_destroy,
541     gdk_pango_attr_embossed_compare
542   };
543
544   if (!klass.type)
545     klass.type = gdk_pango_attr_embossed_type =
546       pango_attr_type_register ("GdkPangoAttrEmbossed");
547
548   result = g_new (GdkPangoAttrEmbossed, 1);
549   result->attr.klass = &klass;
550   result->embossed = embossed;
551   
552   return (PangoAttribute *)result;
553 }