]> Pileus Git - ~andy/gtk/blob - gdk/gdkpango.c
Implement PANGO_UNDERLINE_ERROR (Based on a patch by Nicolas Setton,
[~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 "gdkrgb.h"
24 #include "gdkprivate.h"
25 #include "gdkscreen.h"
26
27 #define GDK_INFO_KEY "gdk-info"
28
29 typedef struct _GdkPangoContextInfo GdkPangoContextInfo;
30
31 struct _GdkPangoContextInfo
32 {
33   GdkColormap *colormap;
34 };
35
36 static PangoAttrType gdk_pango_attr_stipple_type;
37 static PangoAttrType gdk_pango_attr_embossed_type;
38
39 static void gdk_pango_get_item_properties (PangoItem      *item,
40                                            PangoUnderline *uline,
41                                            gboolean       *strikethrough,
42                                            gint           *rise,
43                                            PangoColor     *fg_color,
44                                            gboolean       *fg_set,
45                                            PangoColor     *bg_color,
46                                            gboolean       *bg_set,
47                                            gboolean       *embossed,
48                                            GdkBitmap     **stipple,
49                                            gboolean       *shape_set,
50                                            PangoRectangle *ink_rect,
51                                            PangoRectangle *logical_rect);
52
53 static void
54 gdk_pango_context_destroy (GdkPangoContextInfo *info)
55 {
56   if (info->colormap)
57     g_object_unref (info->colormap);
58   g_free (info);
59 }
60
61 static GdkPangoContextInfo *
62 gdk_pango_context_get_info (PangoContext *context, gboolean create)
63 {
64   GdkPangoContextInfo *info =
65     g_object_get_qdata (G_OBJECT (context),
66                         g_quark_try_string (GDK_INFO_KEY));
67   if (!info && create)
68     {
69       info = g_new (GdkPangoContextInfo, 1);
70       info->colormap = NULL;
71
72       g_object_set_qdata_full (G_OBJECT (context),
73                                g_quark_from_static_string (GDK_INFO_KEY),
74                                info, (GDestroyNotify)gdk_pango_context_destroy);
75     }
76
77   return info;
78 }
79
80 static GdkGC *
81 gdk_pango_get_gc (GdkDrawable    *drawable,
82                   PangoContext   *context,
83                   PangoColor     *fg_color,
84                   GdkBitmap      *stipple,
85                   GdkGC          *base_gc)
86 {
87   GdkGC *result;
88   GdkPangoContextInfo *info;
89   
90   g_return_val_if_fail (context != NULL, NULL);
91
92   info = gdk_pango_context_get_info (context, FALSE);
93
94   if (info == NULL || info->colormap == NULL)
95     {
96       g_warning ("you must set the colormap on a PangoContext before using it to draw a layout");
97       return NULL;
98     }
99
100   result = gdk_gc_new (drawable);
101   gdk_gc_copy (result, base_gc);
102   
103   if (fg_color)
104     {
105       GdkColor color;
106       
107       color.red = fg_color->red;
108       color.green = fg_color->green;
109       color.blue = fg_color->blue;
110
111       gdk_rgb_find_color (info->colormap, &color);
112       gdk_gc_set_foreground (result, &color);
113     }
114
115   if (stipple)
116     {
117       gdk_gc_set_fill (result, GDK_STIPPLED);
118       gdk_gc_set_stipple (result, stipple);
119     }
120   
121   return result;
122 }
123
124 static void
125 gdk_pango_free_gc (PangoContext *context,
126                    GdkGC        *gc)
127 {
128   g_object_unref (gc);
129 }
130
131 /**
132  * gdk_pango_context_set_colormap:
133  * @context: a #PangoContext
134  * @colormap: a #GdkColormap
135  *
136  * Sets the colormap to be used for drawing with @context.
137  *
138  * If you obtained your context from gtk_widget_get_pango_context() or
139  * gtk_widget_create_pango_context(), the colormap will already be set
140  * to the colormap for the widget, so you shouldn't need this
141  * function.
142  **/
143 void
144 gdk_pango_context_set_colormap (PangoContext *context,
145                                 GdkColormap  *colormap)
146 {
147   GdkPangoContextInfo *info;
148   
149   g_return_if_fail (context != NULL);
150
151   info = gdk_pango_context_get_info (context, TRUE);
152   g_return_if_fail (info != NULL);
153   
154   if (info->colormap != colormap)
155     {
156       if (info->colormap)
157         g_object_unref (info->colormap);
158
159       info->colormap = colormap;
160       
161       if (info->colormap)
162         g_object_ref (info->colormap);
163     }
164 }
165
166 static void
167 draw_underline (GdkDrawable    *drawable,
168                 GdkGC          *gc,
169                 PangoUnderline  uline,
170                 int             baseline_y,
171                 int             low_y,
172                 int             start_x,
173                 int             end_x)
174 {
175   switch (uline)
176     {
177     case PANGO_UNDERLINE_NONE:
178       break;
179     case PANGO_UNDERLINE_DOUBLE:
180       gdk_draw_line (drawable, gc,
181                      start_x, baseline_y + 3,
182                      end_x,   baseline_y + 3);
183       /* Fall through */
184     case PANGO_UNDERLINE_SINGLE:
185       gdk_draw_line (drawable, gc,
186                      start_x, baseline_y + 1,
187                      end_x,   baseline_y + 1);
188       break;
189     case PANGO_UNDERLINE_ERROR:
190       {
191         int point_x, point_y;
192         int counter = 0;
193
194         for (point_x = start_x;
195              point_x <= end_x;
196              point_x += 2)
197           {
198             point_y = counter ? baseline_y + 1 : baseline_y + 2;
199             
200             gdk_draw_line (drawable, gc,
201                            point_x, point_y,
202                            MIN (point_x + 1, end_x), point_y);
203             
204             counter = (counter + 1) % 2;
205           }
206       }
207       break;
208     case PANGO_UNDERLINE_LOW:
209       gdk_draw_line (drawable, gc,
210                      start_x, low_y + 1,
211                      end_x,   low_y + 1);
212       break;
213     }
214 }
215
216 /**
217  * gdk_draw_layout_line_with_colors:
218  * @drawable:  the drawable on which to draw the line
219  * @gc:        base graphics to use
220  * @x:         the x position of start of string (in pixels)
221  * @y:         the y position of baseline (in pixels)
222  * @line:      a #PangoLayoutLine
223  * @foreground: foreground override color, or %NULL for none
224  * @background: background override color, or %NULL for none
225  *
226  * Render a #PangoLayoutLine onto a #GdkDrawable, overriding the
227  * layout's normal colors with @foreground and/or @background.
228  * @foreground and @background need not be allocated.
229  */
230 void 
231 gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
232                                   GdkGC            *gc,
233                                   gint              x, 
234                                   gint              y,
235                                   PangoLayoutLine  *line,
236                                   const GdkColor   *foreground,
237                                   const GdkColor   *background)
238 {
239   GSList *tmp_list = line->runs;
240   PangoRectangle overall_rect;
241   PangoRectangle logical_rect;
242   PangoRectangle ink_rect;
243   PangoContext *context;
244   gint x_off = 0;
245   gint rise = 0;
246   gboolean embossed;
247   GdkBitmap *stipple;
248   PangoUnderline last_uline = PANGO_UNDERLINE_NONE;
249   gint uline_start_x = 0;
250   gint uline_end_x = 0;
251   gint uline_end_x_extended = 0;
252   gint last_risen_y = 0;
253   gint low_y = G_MININT;
254   GdkGC *last_fg_gc = NULL;
255   gboolean last_fg_set = FALSE;
256   PangoColor last_fg_color;
257
258   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
259   g_return_if_fail (GDK_IS_GC (gc));
260   g_return_if_fail (line != NULL);
261
262   context = pango_layout_get_context (line->layout);
263   
264   pango_layout_line_get_extents (line,NULL, &overall_rect);
265   
266   while (tmp_list)
267     {
268       PangoUnderline this_uline = PANGO_UNDERLINE_NONE;
269       PangoLayoutRun *run = tmp_list->data;
270       PangoColor fg_color, bg_color;
271       gboolean strike, fg_set, bg_set, shape_set;
272       GdkGC *fg_gc;
273       gint risen_y;
274       
275       tmp_list = tmp_list->next;
276       
277       gdk_pango_get_item_properties (run->item, &this_uline,
278                                      &strike,
279                                      &rise,
280                                      &fg_color, &fg_set,
281                                      &bg_color, &bg_set,
282                                      &embossed,
283                                      &stipple,
284                                      &shape_set,
285                                      &ink_rect,
286                                      &logical_rect);
287
288       /* we subtract the rise because X coordinates are upside down */
289       risen_y = y - rise / PANGO_SCALE;
290       
291       if (!shape_set)
292         {
293           if (this_uline == PANGO_UNDERLINE_NONE)
294             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
295                                         NULL, &logical_rect);
296           else
297             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
298                                         &ink_rect, &logical_rect);
299         }
300
301       if (bg_set || background)
302         {
303           GdkGC *bg_gc;
304           PangoColor tmp = bg_color;
305           
306           if (background)
307             {
308               tmp.red = background->red;
309               tmp.blue = background->blue;
310               tmp.green = background->green;
311             }
312           
313           bg_gc = gdk_pango_get_gc (drawable, context, &tmp, stipple, gc);
314           
315           gdk_draw_rectangle (drawable, bg_gc, TRUE,
316                               x + (x_off + logical_rect.x) / PANGO_SCALE,
317                               risen_y + overall_rect.y / PANGO_SCALE,
318                               logical_rect.width / PANGO_SCALE,
319                               overall_rect.height / PANGO_SCALE);
320
321           if (stipple)
322             gdk_gc_set_fill (bg_gc, GDK_SOLID);
323           
324           gdk_pango_free_gc (context, bg_gc);
325         }
326
327       if (fg_set || stipple || foreground)
328         {
329           PangoColor tmp = fg_color;
330           
331           if (foreground)
332             {
333               tmp.red = foreground->red;
334               tmp.blue = foreground->blue;
335               tmp.green = foreground->green;
336             }
337           
338           fg_gc = gdk_pango_get_gc (drawable, context, (fg_set || foreground) ? &tmp : NULL,
339                                     stipple, gc);
340         }
341       else
342         fg_gc = gc;
343
344       if (!shape_set)
345         {
346           gint gx, gy;
347
348           gx = x + x_off / PANGO_SCALE;
349           gy = risen_y;
350           
351           if (embossed)
352             {
353               PangoColor color = { 65535, 65535, 65535 };
354               GdkGC *white_gc = gdk_pango_get_gc (drawable, context, &color, stipple, fg_gc);
355               gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font,
356                                gx + 1,
357                                gy + 1,
358                                run->glyphs);
359               gdk_pango_free_gc (context, white_gc);
360             }
361           
362           gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
363                            gx, gy,
364                            run->glyphs);
365         }
366
367       if (this_uline != last_uline ||
368           risen_y != last_risen_y ||
369           fg_set != last_fg_set ||
370           (fg_set && (last_fg_color.red != fg_color.red ||
371                       last_fg_color.green != fg_color.green ||
372                       last_fg_color.blue != fg_color.blue)))
373         {
374           /* If only color changes, the underlines extend to the edge
375            * of the logical rectangle so they join up; otherwise they
376            * go 1 pixel beyond the ink rectangle. This doesn't work
377            * for low underlines (they will be at a different y anyways),
378            * so they follow the normal path.
379            */
380           gboolean extend_uline = (this_uline == last_uline &&
381                                    this_uline != PANGO_UNDERLINE_LOW &&
382                                    risen_y == last_risen_y);
383           
384           /* Starting a new underline run
385            */
386           if (last_uline != PANGO_UNDERLINE_NONE)
387             {
388               draw_underline (drawable, last_fg_gc, last_uline,
389                               last_risen_y, low_y,
390                               uline_start_x,
391                               extend_uline ? uline_end_x_extended : uline_end_x);
392             }
393
394           if (this_uline != PANGO_UNDERLINE_NONE)
395             {
396               if (extend_uline)
397                 uline_start_x = x + x_off / PANGO_SCALE;
398               else
399                 uline_start_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1;
400
401               low_y = G_MININT;
402             }
403         }
404
405       /* Update current underline segment information
406        */
407       if (this_uline != PANGO_UNDERLINE_NONE)
408         {
409           uline_end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
410           uline_end_x_extended = x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE - 1;
411         }
412
413       if (this_uline == PANGO_UNDERLINE_LOW)
414         low_y = MAX (low_y, risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE);
415
416       if (strike)
417         {
418           int centerline = logical_rect.y + logical_rect.height / 2;
419           
420           gdk_draw_line (drawable, fg_gc,
421                          x + (x_off + logical_rect.x) / PANGO_SCALE - 1,
422                          risen_y + centerline / PANGO_SCALE,
423                          x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1,
424                          risen_y + centerline / PANGO_SCALE);
425         }
426
427       if (last_fg_gc != gc && last_fg_gc)
428         gdk_pango_free_gc (context, last_fg_gc);
429
430       last_risen_y = risen_y;
431       last_uline = this_uline;
432       last_fg_gc = fg_gc;
433       last_fg_set = fg_set;
434       if (fg_set)
435         last_fg_color = fg_color;
436       
437       x_off += logical_rect.width;
438     }
439
440   /* Finish off any remaining underlines
441    */
442   if (last_uline != PANGO_UNDERLINE_NONE)
443     draw_underline (drawable, last_fg_gc, last_uline, last_risen_y, low_y,
444                     uline_start_x, uline_end_x);
445
446   if (last_fg_gc != gc && last_fg_gc)
447     gdk_pango_free_gc (context, last_fg_gc);
448 }
449
450 /**
451  * gdk_draw_layout_with_colors:
452  * @drawable:  the drawable on which to draw string
453  * @gc:        base graphics context to use
454  * @x:         the X position of the left of the layout (in pixels)
455  * @y:         the Y position of the top of the layout (in pixels)
456  * @layout:    a #PangoLayout
457  * @foreground: foreground override color, or %NULL for none
458  * @background: background override color, or %NULL for none
459  *
460  * Render a #PangoLayout onto a #GdkDrawable, overriding the
461  * layout's normal colors with @foreground and/or @background.
462  * @foreground and @background need not be allocated.
463  *
464  * If you're using GTK+, the ususal way to obtain a #PangoLayout
465  * is gtk_widget_create_pango_layout().
466  */
467 void 
468 gdk_draw_layout_with_colors (GdkDrawable     *drawable,
469                              GdkGC           *gc,
470                              int              x, 
471                              int              y,
472                              PangoLayout     *layout,
473                              const GdkColor  *foreground,
474                              const GdkColor  *background)
475 {
476   PangoLayoutIter *iter;
477   
478   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
479   g_return_if_fail (GDK_IS_GC (gc));
480   g_return_if_fail (PANGO_IS_LAYOUT (layout));
481
482   iter = pango_layout_get_iter (layout);
483   
484   do
485     {
486       PangoRectangle logical_rect;
487       PangoLayoutLine *line;
488       int baseline;
489       
490       line = pango_layout_iter_get_line (iter);
491       
492       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
493       baseline = pango_layout_iter_get_baseline (iter);
494       
495       gdk_draw_layout_line_with_colors (drawable, gc,
496                                         x + logical_rect.x / PANGO_SCALE,
497                                         y + baseline / PANGO_SCALE,
498                                         line,
499                                         foreground,
500                                         background);
501     }
502   while (pango_layout_iter_next_line (iter));
503
504   pango_layout_iter_free (iter);
505 }
506
507 /**
508  * gdk_draw_layout_line:
509  * @drawable:  the drawable on which to draw the line
510  * @gc:        base graphics to use
511  * @x:         the x position of start of string (in pixels)
512  * @y:         the y position of baseline (in pixels)
513  * @line:      a #PangoLayoutLine
514  *
515  * Render a #PangoLayoutLine onto an GDK drawable
516  */
517 void 
518 gdk_draw_layout_line (GdkDrawable      *drawable,
519                       GdkGC            *gc,
520                       gint              x, 
521                       gint              y,
522                       PangoLayoutLine  *line)
523 {
524   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
525   g_return_if_fail (GDK_IS_GC (gc));
526   g_return_if_fail (line != NULL);
527   
528   gdk_draw_layout_line_with_colors (drawable, gc, x, y, line, NULL, NULL);
529 }
530
531 /**
532  * gdk_draw_layout:
533  * @drawable:  the drawable on which to draw string
534  * @gc:        base graphics context to use
535  * @x:         the X position of the left of the layout (in pixels)
536  * @y:         the Y position of the top of the layout (in pixels)
537  * @layout:    a #PangoLayout
538  *
539  * Render a #PangoLayout onto a GDK drawable
540  *
541  * If you're using GTK+, the ususal way to obtain a #PangoLayout
542  * is gtk_widget_create_pango_layout().
543  */
544 void 
545 gdk_draw_layout (GdkDrawable     *drawable,
546                  GdkGC           *gc,
547                  int              x, 
548                  int              y,
549                  PangoLayout     *layout)
550 {
551   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
552   g_return_if_fail (GDK_IS_GC (gc));
553   g_return_if_fail (PANGO_IS_LAYOUT (layout));
554
555   gdk_draw_layout_with_colors (drawable, gc, x, y, layout, NULL, NULL);
556 }
557
558 static void
559 gdk_pango_get_item_properties (PangoItem      *item,
560                                PangoUnderline *uline,
561                                gboolean       *strikethrough,
562                                gint           *rise,
563                                PangoColor     *fg_color,
564                                gboolean       *fg_set,
565                                PangoColor     *bg_color,
566                                gboolean       *bg_set,
567                                gboolean       *embossed,
568                                GdkBitmap     **stipple,
569                                gboolean       *shape_set,
570                                PangoRectangle *ink_rect,
571                                PangoRectangle *logical_rect)
572 {
573   GSList *tmp_list = item->analysis.extra_attrs;
574
575   if (strikethrough)
576       *strikethrough = FALSE;
577   
578   if (fg_set)
579     *fg_set = FALSE;
580   
581   if (bg_set)
582     *bg_set = FALSE;
583
584   if (shape_set)
585     *shape_set = FALSE;
586
587   if (rise)
588     *rise = 0;
589
590   if (embossed)
591     *embossed = FALSE;
592
593   if (stipple)
594     *stipple = NULL;
595   
596   while (tmp_list)
597     {
598       PangoAttribute *attr = tmp_list->data;
599
600       switch (attr->klass->type)
601         {
602         case PANGO_ATTR_UNDERLINE:
603           if (uline)
604             *uline = ((PangoAttrInt *)attr)->value;
605           break;
606
607         case PANGO_ATTR_STRIKETHROUGH:
608             if (strikethrough)
609                 *strikethrough = ((PangoAttrInt *)attr)->value;
610             break;
611             
612         case PANGO_ATTR_FOREGROUND:
613           if (fg_color)
614             *fg_color = ((PangoAttrColor *)attr)->color;
615           if (fg_set)
616             *fg_set = TRUE;
617           
618           break;
619           
620         case PANGO_ATTR_BACKGROUND:
621           if (bg_color)
622             *bg_color = ((PangoAttrColor *)attr)->color;
623           if (bg_set)
624             *bg_set = TRUE;
625           
626           break;
627
628         case PANGO_ATTR_SHAPE:
629           if (shape_set)
630             *shape_set = TRUE;
631           if (logical_rect)
632             *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
633           if (ink_rect)
634             *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
635           break;
636
637         case PANGO_ATTR_RISE:
638           if (rise)
639             *rise = ((PangoAttrInt *)attr)->value;
640           break;
641           
642         default:
643           /* stipple_type and embossed_type aren't necessarily
644            * initialized, but they are 0, which is an
645            * invalid type so won't occur. 
646            */
647           if (stipple && attr->klass->type == gdk_pango_attr_stipple_type)
648             {
649               *stipple = ((GdkPangoAttrStipple*)attr)->stipple;
650             }
651           else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type)
652             {
653               *embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
654             }
655           break;
656         }
657       tmp_list = tmp_list->next;
658     }
659 }
660
661
662 static PangoAttribute *
663 gdk_pango_attr_stipple_copy (const PangoAttribute *attr)
664 {
665   const GdkPangoAttrStipple *src = (const GdkPangoAttrStipple*) attr;
666
667   return gdk_pango_attr_stipple_new (src->stipple);
668 }
669
670 static void
671 gdk_pango_attr_stipple_destroy (PangoAttribute *attr)
672 {
673   GdkPangoAttrStipple *st = (GdkPangoAttrStipple*) attr;
674
675   if (st->stipple)
676     g_object_unref (st->stipple);
677   
678   g_free (attr);
679 }
680
681 static gboolean
682 gdk_pango_attr_stipple_compare (const PangoAttribute *attr1,
683                                     const PangoAttribute *attr2)
684 {
685   const GdkPangoAttrStipple *a = (const GdkPangoAttrStipple*) attr1;
686   const GdkPangoAttrStipple *b = (const GdkPangoAttrStipple*) attr2;
687
688   return a->stipple == b->stipple;
689 }
690
691 /**
692  * gdk_pango_attr_stipple_new:
693  * @stipple: a bitmap to be set as stipple
694  *
695  * Creates a new attribute containing a stipple bitmap to be used when
696  * rendering the text.
697  *
698  * Return value: new #PangoAttribute
699  **/
700
701 PangoAttribute *
702 gdk_pango_attr_stipple_new (GdkBitmap *stipple)
703 {
704   GdkPangoAttrStipple *result;
705   
706   static PangoAttrClass klass = {
707     0,
708     gdk_pango_attr_stipple_copy,
709     gdk_pango_attr_stipple_destroy,
710     gdk_pango_attr_stipple_compare
711   };
712
713   if (!klass.type)
714     klass.type = gdk_pango_attr_stipple_type =
715       pango_attr_type_register ("GdkPangoAttrStipple");
716
717   result = g_new (GdkPangoAttrStipple, 1);
718   result->attr.klass = &klass;
719
720   if (stipple)
721     g_object_ref (stipple);
722   
723   result->stipple = stipple;
724
725   return (PangoAttribute *)result;
726 }
727
728 static PangoAttribute *
729 gdk_pango_attr_embossed_copy (const PangoAttribute *attr)
730 {
731   const GdkPangoAttrEmbossed *e = (const GdkPangoAttrEmbossed*) attr;
732
733   return gdk_pango_attr_embossed_new (e->embossed);
734 }
735
736 static void
737 gdk_pango_attr_embossed_destroy (PangoAttribute *attr)
738 {
739   g_free (attr);
740 }
741
742 static gboolean
743 gdk_pango_attr_embossed_compare (const PangoAttribute *attr1,
744                                  const PangoAttribute *attr2)
745 {
746   const GdkPangoAttrEmbossed *e1 = (const GdkPangoAttrEmbossed*) attr1;
747   const GdkPangoAttrEmbossed *e2 = (const GdkPangoAttrEmbossed*) attr2;
748
749   return e1->embossed == e2->embossed;
750 }
751
752 /**
753  * gdk_pango_attr_embossed_new:
754  * @embossed: a bitmap to be set as embossed
755  *
756  * Creates a new attribute containing a embossed bitmap to be used when
757  * rendering the text.
758  *
759  * Return value: new #PangoAttribute
760  **/
761
762 PangoAttribute *
763 gdk_pango_attr_embossed_new (gboolean embossed)
764 {
765   GdkPangoAttrEmbossed *result;
766   
767   static PangoAttrClass klass = {
768     0,
769     gdk_pango_attr_embossed_copy,
770     gdk_pango_attr_embossed_destroy,
771     gdk_pango_attr_embossed_compare
772   };
773
774   if (!klass.type)
775     klass.type = gdk_pango_attr_embossed_type =
776       pango_attr_type_register ("GdkPangoAttrEmbossed");
777
778   result = g_new (GdkPangoAttrEmbossed, 1);
779   result->attr.klass = &klass;
780   result->embossed = embossed;
781   
782   return (PangoAttribute *)result;
783 }
784
785 /* Get a clip region to draw only part of a layout. index_ranges
786  * contains alternating range starts/stops. The region is the
787  * region which contains the given ranges, i.e. if you draw with the
788  * region as clip, only the given ranges are drawn.
789  */
790
791 /**
792  * gdk_pango_layout_line_get_clip_region:
793  * @line: a #PangoLayoutLine 
794  * @x_origin: X pixel where you intend to draw the layout line with this clip
795  * @y_origin: baseline pixel where you intend to draw the layout line with this clip
796  * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
797  * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
798  * 
799  * Obtains a clip region which contains the areas where the given
800  * ranges of text would be drawn. @x_origin and @y_origin are the same
801  * position you would pass to gdk_draw_layout_line(). @index_ranges
802  * should contain ranges of bytes in the layout's text. The clip
803  * region will include space to the left or right of the line (to the
804  * layout bounding box) if you have indexes above or below the indexes
805  * contained inside the line. This is to draw the selection all the way
806  * to the side of the layout. However, the clip region is in line coordinates,
807  * not layout coordinates.
808  * 
809  * Return value: a clip region containing the given ranges
810  **/
811 GdkRegion*
812 gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line,
813                                        gint             x_origin,
814                                        gint             y_origin,
815                                        gint            *index_ranges,
816                                        gint             n_ranges)
817 {
818   GdkRegion *clip_region;
819   gint i;
820   PangoRectangle logical_rect;
821   PangoLayoutIter *iter;
822   gint baseline;
823   
824   g_return_val_if_fail (line != NULL, NULL);
825   g_return_val_if_fail (index_ranges != NULL, NULL);
826   
827   clip_region = gdk_region_new ();
828
829   iter = pango_layout_get_iter (line->layout);
830   while (pango_layout_iter_get_line (iter) != line)
831     pango_layout_iter_next_line (iter);
832   
833   pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
834   baseline = pango_layout_iter_get_baseline (iter);
835   
836   pango_layout_iter_free (iter);
837   
838   i = 0;
839   while (i < n_ranges)
840     {  
841       gint *pixel_ranges = NULL;
842       gint n_pixel_ranges = 0;
843       gint j;
844
845       /* Note that get_x_ranges returns layout coordinates
846        */
847       pango_layout_line_get_x_ranges (line,
848                                       index_ranges[i*2],
849                                       index_ranges[i*2+1],
850                                       &pixel_ranges, &n_pixel_ranges);
851   
852       for (j=0; j < n_pixel_ranges; j++)
853         {
854           GdkRectangle rect;
855           
856           rect.x = x_origin + pixel_ranges[2*j] / PANGO_SCALE - logical_rect.x / PANGO_SCALE;
857           rect.y = y_origin - (baseline / PANGO_SCALE - logical_rect.y / PANGO_SCALE);
858           rect.width = (pixel_ranges[2*j + 1] - pixel_ranges[2*j]) / PANGO_SCALE;
859           rect.height = logical_rect.height / PANGO_SCALE;
860           
861           gdk_region_union_with_rect (clip_region, &rect);
862         }
863
864       g_free (pixel_ranges);
865       ++i;
866     }
867
868   return clip_region;
869 }
870
871 /**
872  * gdk_pango_layout_get_clip_region:
873  * @layout: a #PangoLayout 
874  * @x_origin: X pixel where you intend to draw the layout with this clip
875  * @y_origin: Y pixel where you intend to draw the layout with this clip
876  * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
877  * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
878  * 
879  * Obtains a clip region which contains the areas where the given ranges
880  * of text would be drawn. @x_origin and @y_origin are the same position
881  * you would pass to gdk_draw_layout_line(). @index_ranges should contain
882  * ranges of bytes in the layout's text.
883  * 
884  * Return value: a clip region containing the given ranges
885  **/
886 GdkRegion*
887 gdk_pango_layout_get_clip_region (PangoLayout *layout,
888                                   gint         x_origin,
889                                   gint         y_origin,
890                                   gint        *index_ranges,
891                                   gint         n_ranges)
892 {
893   PangoLayoutIter *iter;  
894   GdkRegion *clip_region;
895   
896   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
897   g_return_val_if_fail (index_ranges != NULL, NULL);
898   
899   clip_region = gdk_region_new ();
900   
901   iter = pango_layout_get_iter (layout);
902   
903   do
904     {
905       PangoRectangle logical_rect;
906       PangoLayoutLine *line;
907       GdkRegion *line_region;
908       gint baseline;
909       
910       line = pango_layout_iter_get_line (iter);      
911
912       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
913       baseline = pango_layout_iter_get_baseline (iter);      
914
915       line_region = gdk_pango_layout_line_get_clip_region (line,
916                                                            x_origin + logical_rect.x / PANGO_SCALE,
917                                                            y_origin + baseline / PANGO_SCALE,
918                                                            index_ranges,
919                                                            n_ranges);
920
921       gdk_region_union (clip_region, line_region);
922       gdk_region_destroy (line_region);
923     }
924   while (pango_layout_iter_next_line (iter));
925
926   pango_layout_iter_free (iter);
927
928   return clip_region;
929 }
930
931 /**
932  * gdk_pango_context_get:
933  * 
934  * Creates a #PangoContext for the default GDK screen.
935  *
936  * The context must be freed when you're finished with it.
937  * 
938  * When using GTK+, normally you should use gtk_widget_get_pango_context()
939  * instead of this function, to get the appropriate context for
940  * the widget you intend to render text onto.
941  * 
942  * Return value: a new #PangoContext for the default display
943  **/
944 PangoContext *
945 gdk_pango_context_get (void)
946 {
947   return gdk_pango_context_get_for_screen (gdk_screen_get_default ());
948 }