X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fgdkpango.c;h=ca5745beb27f012eb1d47b549f3e367d59df4c12;hb=c37de57f14d57ece9d0dd80a01a33641d71def93;hp=85c582a46655f09505ab3c9f0c9b6a62f8b3d06f;hpb=df4fc3672127660c1d47e4225297d00abbee84eb;p=~andy%2Fgtk diff --git a/gdk/gdkpango.c b/gdk/gdkpango.c index 85c582a46..ca5745beb 100644 --- a/gdk/gdkpango.c +++ b/gdk/gdkpango.c @@ -2,368 +2,369 @@ * Copyright (C) 2000 Red Hat, Inc. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . */ -#include "gdkcolor.h" -#include "gdkgc.h" -#include "gdkpango.h" -#include "gdkprivate.h" +#include "config.h" -#define GDK_INFO_KEY "gdk-info" +#include "gdkpango.h" -typedef struct _GdkPangoContextInfo GdkPangoContextInfo; +#include "gdkscreen.h" +#include "gdkintl.h" -struct _GdkPangoContextInfo -{ - GdkColormap *colormap; -}; +#include +#include -static void gdk_pango_get_item_properties (PangoItem *item, - PangoUnderline *uline, - PangoAttrColor *fg_color, - gboolean *fg_set, - PangoAttrColor *bg_color, - gboolean *bg_set); -static void -gdk_pango_context_destroy (GdkPangoContextInfo *info) -{ - gdk_colormap_unref (info->colormap); - g_free (info); -} +/** + * SECTION:pango_interaction + * @Short_description: Using Pango in GDK + * @Title: Pango Interaction + * + * Pango is the text layout system used by GDK and GTK+. The functions + * and types in this section are used to obtain clip regions for + * #PangoLayouts, and to get #PangoContexts that can be used with + * GDK. + * + * Creating a #PangoLayout object is the first step in rendering text, + * and requires getting a handle to a #PangoContext. For GTK+ programs, + * you'll usually want to use gtk_widget_get_pango_context(), or + * gtk_widget_create_pango_layout(), rather than using the lowlevel + * gdk_pango_context_get_for_screen(). Once you have a #PangoLayout, you + * can set the text and attributes of it with Pango functions like + * pango_layout_set_text() and get its size with pango_layout_get_size(). + * (Note that Pango uses a fixed point system internally, so converting + * between Pango units and pixels using PANGO_SCALE or the PANGO_PIXELS() macro.) + * + * Rendering a Pango layout is done most simply with pango_cairo_show_layout(); + * you can also draw pieces of the layout with pango_cairo_show_layout_line(). + * + * Draw transformed text with Pango and cairo + * + * + * #define RADIUS 100 + * #define N_WORDS 10 + * #define FONT "Sans Bold 18" + * + * PangoContext *context; + * PangoLayout *layout; + * PangoFontDescription *desc; + * + * double radius; + * int width, height; + * int i; + * + * /* Set up a transformation matrix so that the user space coordinates for + * * where we are drawing are [-RADIUS, RADIUS], [-RADIUS, RADIUS] + * * We first center, then change the scale */ + * + * width = gdk_window_get_width (window); + * height = gdk_window_get_height (window); + * radius = MIN (width, height) / 2.; + * + * cairo_translate (cr, + * radius + (width - 2 * radius) / 2, + * radius + (height - 2 * radius) / 2); + * cairo_scale (cr, radius / RADIUS, radius / RADIUS); + * + * /* Create a PangoLayout, set the font and text */ + * context = gdk_pango_context_get_for_screen (screen); + * layout = pango_layout_new (context); + * pango_layout_set_text (layout, "Text", -1); + * desc = pango_font_description_from_string (FONT); + * pango_layout_set_font_description (layout, desc); + * pango_font_description_free (desc); + * + * /* Draw the layout N_WORDS times in a circle */ + * for (i = 0; i < N_WORDS; i++) + * { + * double red, green, blue; + * double angle = 2 * G_PI * i / n_words; + * + * cairo_save (cr); + * + * /* Gradient from red at angle == 60 to blue at angle == 300 */ + * red = (1 + cos (angle - 60)) / 2; + * green = 0; + * blue = 1 - red; + * + * cairo_set_source_rgb (cr, red, green, blue); + * cairo_rotate (cr, angle); + * + * /* Inform Pango to re-layout the text with the new transformation matrix */ + * pango_cairo_update_layout (cr, layout); + * + * pango_layout_get_size (layout, &width, &height); + * + * cairo_move_to (cr, - width / 2 / PANGO_SCALE, - DEFAULT_TEXT_RADIUS); + * pango_cairo_show_layout (cr, layout); + * + * cairo_restore (cr); + * } + * + * g_object_unref (layout); + * g_object_unref (context); + * + * + *
+ * Output of <xref linkend="rotated-example"/> + * + *
+ */ -static GdkPangoContextInfo * -gdk_pango_context_get_info (PangoContext *context, gboolean create) +/* Get a clip region to draw only part of a layout. index_ranges + * contains alternating range starts/stops. The region is the + * region which contains the given ranges, i.e. if you draw with the + * region as clip, only the given ranges are drawn. + */ +static cairo_region_t* +layout_iter_get_line_clip_region (PangoLayoutIter *iter, + gint x_origin, + gint y_origin, + const gint *index_ranges, + gint n_ranges) { - GdkPangoContextInfo *info = - g_object_get_qdata (G_OBJECT (context), - g_quark_try_string (GDK_INFO_KEY)); - if (!info && create) - { - info = g_new (GdkPangoContextInfo, 1); - info->colormap = NULL; - - g_object_set_qdata_full (G_OBJECT (context), - g_quark_from_static_string (GDK_INFO_KEY), - info, (GDestroyNotify)gdk_pango_context_destroy); + PangoLayoutLine *line; + cairo_region_t *clip_region; + PangoRectangle logical_rect; + gint baseline; + gint i; + + line = pango_layout_iter_get_line_readonly (iter); + + clip_region = cairo_region_create (); + + pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); + baseline = pango_layout_iter_get_baseline (iter); + + i = 0; + while (i < n_ranges) + { + gint *pixel_ranges = NULL; + gint n_pixel_ranges = 0; + gint j; + + /* Note that get_x_ranges returns layout coordinates + */ + if (index_ranges[i*2+1] >= line->start_index && + index_ranges[i*2] < line->start_index + line->length) + pango_layout_line_get_x_ranges (line, + index_ranges[i*2], + index_ranges[i*2+1], + &pixel_ranges, &n_pixel_ranges); + + for (j = 0; j < n_pixel_ranges; j++) + { + GdkRectangle rect; + int x_off, y_off; + + x_off = PANGO_PIXELS (pixel_ranges[2*j] - logical_rect.x); + y_off = PANGO_PIXELS (baseline - logical_rect.y); + + rect.x = x_origin + x_off; + rect.y = y_origin - y_off; + rect.width = PANGO_PIXELS (pixel_ranges[2*j + 1] - logical_rect.x) - x_off; + rect.height = PANGO_PIXELS (baseline - logical_rect.y + logical_rect.height) - y_off; + + cairo_region_union_rectangle (clip_region, &rect); + } + + g_free (pixel_ranges); + ++i; } - - return info; + return clip_region; } -static GdkGC * -gdk_pango_get_gc (PangoContext *context, - PangoAttrColor *fg_color, - GdkGC *base_gc) +/** + * gdk_pango_layout_line_get_clip_region: (skip) + * @line: a #PangoLayoutLine + * @x_origin: X pixel where you intend to draw the layout line with this clip + * @y_origin: baseline pixel where you intend to draw the layout line with this clip + * @index_ranges: (array): array of byte indexes into the layout, + * where even members of array are start indexes and odd elements + * are end indexes + * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges + * + * Obtains a clip region which contains the areas where the given + * ranges of text would be drawn. @x_origin and @y_origin are the top left + * position of the layout. @index_ranges + * should contain ranges of bytes in the layout's text. The clip + * region will include space to the left or right of the line (to the + * layout bounding box) if you have indexes above or below the indexes + * contained inside the line. This is to draw the selection all the way + * to the side of the layout. However, the clip region is in line coordinates, + * not layout coordinates. + * + * Note that the regions returned correspond to logical extents of the text + * ranges, not ink extents. So the drawn line may in fact touch areas out of + * the clip region. The clip region is mainly useful for highlightling parts + * of text, such as when text is selected. + * + * Return value: a clip region containing the given ranges + **/ +cairo_region_t* +gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line, + gint x_origin, + gint y_origin, + const gint *index_ranges, + gint n_ranges) { - GdkPangoContextInfo *info; - GdkColormap *colormap; - GdkColor color; + cairo_region_t *clip_region; + PangoLayoutIter *iter; - g_return_val_if_fail (context != NULL, NULL); - - info = gdk_pango_context_get_info (context, FALSE); - - if (info && info->colormap) - colormap = info->colormap; - else - colormap = gdk_colormap_get_system(); - - /* FIXME. FIXME. FIXME. Only works for true color */ - - color.red = fg_color->red; - color.green = fg_color->green; - color.blue = fg_color->blue; + g_return_val_if_fail (line != NULL, NULL); + g_return_val_if_fail (index_ranges != NULL, NULL); - if (gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) - { - GdkGC *result = gdk_gc_new (gdk_parent_root); - gdk_gc_copy (result, base_gc); - gdk_gc_set_foreground (result, &color); - - return result; - } - else - return gdk_gc_ref (base_gc); -} - -static void -gdk_pango_free_gc (PangoContext *context, - GdkGC *gc) -{ - gdk_gc_unref (gc); -} - -void -gdk_pango_context_set_colormap (PangoContext *context, - GdkColormap *colormap) -{ - GdkPangoContextInfo *info; + iter = pango_layout_get_iter (line->layout); + while (pango_layout_iter_get_line_readonly (iter) != line) + pango_layout_iter_next_line (iter); - g_return_if_fail (context != NULL); + clip_region = layout_iter_get_line_clip_region(iter, x_origin, y_origin, index_ranges, n_ranges); - info = gdk_pango_context_get_info (context, TRUE); - g_return_if_fail (info != NULL); - - if (info->colormap != colormap) - { - if (info->colormap) - gdk_colormap_unref (info->colormap); + pango_layout_iter_free (iter); - info->colormap = colormap; - - if (info->colormap) - gdk_colormap_ref (info->colormap); - } + return clip_region; } - /** - * gdk_draw_layout_line: - * @drawable: the drawable on which to draw the line - * @gc: base graphics to use - * @x: the x position of start of string (in pixels) - * @y: the y position of baseline (in pixels) - * @line: a #PangoLayoutLine - * - * Render a #PangoLayoutLine onto an GDK drawable - */ -void -gdk_draw_layout_line (GdkDrawable *drawable, - GdkGC *gc, - gint x, - gint y, - PangoLayoutLine *line) + * gdk_pango_layout_get_clip_region: (skip) + * @layout: a #PangoLayout + * @x_origin: X pixel where you intend to draw the layout with this clip + * @y_origin: Y pixel where you intend to draw the layout with this clip + * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes + * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges + * + * Obtains a clip region which contains the areas where the given ranges + * of text would be drawn. @x_origin and @y_origin are the top left point + * to center the layout. @index_ranges should contain + * ranges of bytes in the layout's text. + * + * Note that the regions returned correspond to logical extents of the text + * ranges, not ink extents. So the drawn layout may in fact touch areas out of + * the clip region. The clip region is mainly useful for highlightling parts + * of text, such as when text is selected. + * + * Return value: a clip region containing the given ranges + **/ +cairo_region_t* +gdk_pango_layout_get_clip_region (PangoLayout *layout, + gint x_origin, + gint y_origin, + const gint *index_ranges, + gint n_ranges) { - GSList *tmp_list = line->runs; - PangoRectangle overall_rect; - PangoRectangle logical_rect; - PangoRectangle ink_rect; - PangoContext *context; - gint x_off = 0; - - g_return_if_fail (drawable != NULL); - g_return_if_fail (gc != NULL); - g_return_if_fail (line != NULL); - - context = pango_layout_get_context (line->layout); + PangoLayoutIter *iter; + cairo_region_t *clip_region; + + g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL); + g_return_val_if_fail (index_ranges != NULL, NULL); + + clip_region = cairo_region_create (); - pango_layout_line_get_extents (line,NULL, &overall_rect); + iter = pango_layout_get_iter (layout); - while (tmp_list) + do { - PangoUnderline uline = PANGO_UNDERLINE_NONE; - PangoLayoutRun *run = tmp_list->data; - PangoAttrColor fg_color, bg_color; - gboolean fg_set, bg_set; - GdkGC *fg_gc; + PangoRectangle logical_rect; + cairo_region_t *line_region; + gint baseline; - tmp_list = tmp_list->next; + pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); + baseline = pango_layout_iter_get_baseline (iter); - gdk_pango_get_item_properties (run->item, &uline, &fg_color, &fg_set, &bg_color, &bg_set); + line_region = layout_iter_get_line_clip_region(iter, + x_origin + PANGO_PIXELS (logical_rect.x), + y_origin + PANGO_PIXELS (baseline), + index_ranges, + n_ranges); - if (fg_set) - fg_gc = gdk_pango_get_gc (context, &fg_color, gc); - else - fg_gc = gc; - - if (uline == PANGO_UNDERLINE_NONE) - pango_glyph_string_extents (run->glyphs, run->item->analysis.font, - NULL, &logical_rect); - else - pango_glyph_string_extents (run->glyphs, run->item->analysis.font, - &ink_rect, &logical_rect); - - if (bg_set) - { - GdkGC *bg_gc = gdk_pango_get_gc (context, &bg_color, gc); - - gdk_draw_rectangle (drawable, bg_gc, TRUE, - x + (x_off + logical_rect.x) / PANGO_SCALE, - y + overall_rect.y / PANGO_SCALE, - logical_rect.width / PANGO_SCALE, - overall_rect.height / PANGO_SCALE); - - gdk_pango_free_gc (context, bg_gc); - } - - gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font, - x + x_off / PANGO_SCALE, y, run->glyphs); + cairo_region_union (clip_region, line_region); + cairo_region_destroy (line_region); + } + while (pango_layout_iter_next_line (iter)); - switch (uline) - { - case PANGO_UNDERLINE_NONE: - break; - case PANGO_UNDERLINE_DOUBLE: - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4); - /* Fall through */ - case PANGO_UNDERLINE_SINGLE: - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2); - break; - case PANGO_UNDERLINE_LOW: - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2); - break; - } + pango_layout_iter_free (iter); - if (fg_set) - gdk_pango_free_gc (context, fg_gc); - - x_off += logical_rect.width; - } + return clip_region; } /** - * gdk_draw_layout: - * @drawable: the drawable on which to draw string - * @gc: base graphics context to use - * @x: the X position of the left of the layout (in pixels) - * @y: the Y position of the top of the layout (in pixels) - * @layout: a #PangoLayout + * gdk_pango_context_get: + * + * Creates a #PangoContext for the default GDK screen. * - * Render a #PangoLayout onto a GDK drawable - */ -void -gdk_draw_layout (GdkDrawable *drawable, - GdkGC *gc, - int x, - int y, - PangoLayout *layout) + * The context must be freed when you're finished with it. + * + * When using GTK+, normally you should use gtk_widget_get_pango_context() + * instead of this function, to get the appropriate context for + * the widget you intend to render text onto. + * + * The newly created context will have the default font options (see + * #cairo_font_options_t) for the default screen; if these options + * change it will not be updated. Using gtk_widget_get_pango_context() + * is more convenient if you want to keep a context around and track + * changes to the screen's font rendering settings. + * + * Return value: (transfer full): a new #PangoContext for the default display + **/ +PangoContext * +gdk_pango_context_get (void) { - PangoRectangle logical_rect; - GSList *tmp_list; - PangoAlignment align; - gint indent; - gint width; - gint y_offset = 0; - gboolean first = FALSE; - - g_return_if_fail (drawable != NULL); - g_return_if_fail (gc != NULL); - g_return_if_fail (layout != NULL); - - indent = pango_layout_get_indent (layout); - width = pango_layout_get_width (layout); - align = pango_layout_get_alignment (layout); - - if (width == -1 && align != PANGO_ALIGN_LEFT) - { - pango_layout_get_extents (layout, NULL, &logical_rect); - width = logical_rect.width; - } - - tmp_list = pango_layout_get_lines (layout); - while (tmp_list) - { - PangoLayoutLine *line = tmp_list->data; - int x_offset; - - pango_layout_line_get_extents (line, NULL, &logical_rect); - - if (width != 1 && align == PANGO_ALIGN_RIGHT) - x_offset = width - logical_rect.width; - else if (width != 1 && align == PANGO_ALIGN_CENTER) - x_offset = (width - logical_rect.width) / 2; - else - x_offset = 0; + return gdk_pango_context_get_for_screen (gdk_screen_get_default ()); +} - if (first) - { - if (indent > 0) - { - if (align == PANGO_ALIGN_LEFT) - x_offset += indent; - else - x_offset -= indent; - } +/** + * gdk_pango_context_get_for_screen: + * @screen: the #GdkScreen for which the context is to be created. + * + * Creates a #PangoContext for @screen. + * + * The context must be freed when you're finished with it. + * + * When using GTK+, normally you should use gtk_widget_get_pango_context() + * instead of this function, to get the appropriate context for + * the widget you intend to render text onto. + * + * The newly created context will have the default font options + * (see #cairo_font_options_t) for the screen; if these options + * change it will not be updated. Using gtk_widget_get_pango_context() + * is more convenient if you want to keep a context around and track + * changes to the screen's font rendering settings. + * + * Return value: (transfer full): a new #PangoContext for @screen + * + * Since: 2.2 + **/ +PangoContext * +gdk_pango_context_get_for_screen (GdkScreen *screen) +{ + PangoFontMap *fontmap; + PangoContext *context; + const cairo_font_options_t *options; + double dpi; - first = FALSE; - } - else - { - if (indent < 0) - { - if (align == PANGO_ALIGN_LEFT) - x_offset -= indent; - else - x_offset += indent; - } - } - - gdk_draw_layout_line (drawable, gc, - x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE, - line); + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); - y_offset += logical_rect.height; - tmp_list = tmp_list->next; - } -} + fontmap = pango_cairo_font_map_get_default (); + context = pango_font_map_create_context (fontmap); -static void -gdk_pango_get_item_properties (PangoItem *item, - PangoUnderline *uline, - PangoAttrColor *fg_color, - gboolean *fg_set, - PangoAttrColor *bg_color, - gboolean *bg_set) -{ - GSList *tmp_list = item->extra_attrs; + options = gdk_screen_get_font_options (screen); + pango_cairo_context_set_font_options (context, options); - if (fg_set) - *fg_set = FALSE; - - if (bg_set) - *bg_set = FALSE; - - while (tmp_list) - { - PangoAttribute *attr = tmp_list->data; + dpi = gdk_screen_get_resolution (screen); + pango_cairo_context_set_resolution (context, dpi); - switch (attr->klass->type) - { - case PANGO_ATTR_UNDERLINE: - if (uline) - *uline = ((PangoAttrInt *)attr)->value; - break; - - case PANGO_ATTR_FOREGROUND: - if (fg_color) - *fg_color = *((PangoAttrColor *)attr); - if (fg_set) - *fg_set = TRUE; - - break; - - case PANGO_ATTR_BACKGROUND: - if (bg_color) - *bg_color = *((PangoAttrColor *)attr); - if (bg_set) - *bg_set = TRUE; - - break; - - default: - break; - } - tmp_list = tmp_list->next; - } + return context; } -