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);
+ *
+ *
+ *
+ */
-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;
}
-