-#include "gdkalias.h"
-
-#define GDK_INFO_KEY "gdk-info"
-
-/* We have various arrays indexed by render part; if PangoRenderPart
- * is extended, we want to make sure not to overwrite the end of
- * those arrays.
- */
-#define MAX_RENDER_PART PANGO_RENDER_PART_STRIKETHROUGH
-
-struct _GdkPangoRendererPrivate
-{
- GdkScreen *screen;
-
- /* GdkPangoRenderer specific state */
- PangoColor override_color[MAX_RENDER_PART + 1];
- gboolean override_color_set[MAX_RENDER_PART + 1];
-
- GdkBitmap *stipple[MAX_RENDER_PART + 1];
- gboolean embossed;
-
- cairo_t *cr;
- PangoRenderPart last_part;
-
- /* Current target */
- GdkDrawable *drawable;
- GdkGC *base_gc;
-
- gboolean gc_changed;
-};
-
-static PangoAttrType gdk_pango_attr_stipple_type;
-static PangoAttrType gdk_pango_attr_embossed_type;
-
-enum {
- PROP_0,
- PROP_SCREEN
-};
-
-G_DEFINE_TYPE (GdkPangoRenderer, gdk_pango_renderer, PANGO_TYPE_RENDERER)
-
-static void
-gdk_pango_renderer_finalize (GObject *object)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
- int i;
-
- if (priv->base_gc)
- g_object_unref (priv->base_gc);
- if (priv->drawable)
- g_object_unref (priv->drawable);
-
- for (i = 0; i <= MAX_RENDER_PART; i++)
- if (priv->stipple[i])
- g_object_unref (priv->stipple[i]);
-
- G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->finalize (object);
-}
-
-static GObject*
-gdk_pango_renderer_constructor (GType type,
- guint n_construct_properties,
- GObjectConstructParam *construct_params)
-{
- GObject *object;
- GdkPangoRenderer *gdk_renderer;
-
- object = (* G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->constructor) (type,
- n_construct_properties,
- construct_params);
-
- gdk_renderer = GDK_PANGO_RENDERER (object);
-
- if (!gdk_renderer->priv->screen)
- {
- g_warning ("Screen must be specified at construct time for GdkPangoRenderer");
- gdk_renderer->priv->screen = gdk_screen_get_default ();
- }
-
- return object;
-}
-
-/* Adjusts matrix and color for the renderer to draw the secondary
- * "shadow" copy for embossed text */
-static void
-emboss_context (cairo_t *cr)
-{
- cairo_matrix_t tmp_matrix;
-
- /* The gymnastics here to adjust the matrix are because we want
- * to offset by +1,+1 in device-space, not in user-space,
- * so we can't just draw the layout at x + 1, y + 1
- */
- cairo_get_matrix (cr, &tmp_matrix);
- tmp_matrix.x0 += 1.0;
- tmp_matrix.y0 += 1.0;
- cairo_set_matrix (cr, &tmp_matrix);
-
- cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
-}
-
-static inline gboolean
-color_equal (PangoColor *c1, PangoColor *c2)
-{
- if (!c1 && !c2)
- return TRUE;
-
- if (c1 && c2 &&
- c1->red == c2->red &&
- c1->green == c2->green &&
- c1->blue == c2->blue)
- return TRUE;
-
- return FALSE;
-}
-
-static cairo_t *
-get_cairo_context (GdkPangoRenderer *gdk_renderer,
- PangoRenderPart part)
-{
- PangoRenderer *renderer = PANGO_RENDERER (gdk_renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
-
- if (!priv->cr)
- {
- const PangoMatrix *matrix;
-
- priv->cr = gdk_cairo_create (priv->drawable);
-
- matrix = pango_renderer_get_matrix (renderer);
- if (matrix)
- {
- cairo_matrix_t cairo_matrix;
-
- cairo_matrix_init (&cairo_matrix,
- matrix->xx, matrix->yx,
- matrix->xy, matrix->yy,
- matrix->x0, matrix->y0);
- cairo_set_matrix (priv->cr, &cairo_matrix);
- }
- }
-
- if (part != priv->last_part)
- {
- PangoColor *pango_color;
- GdkColor *color;
- GdkColor tmp_color;
- gboolean changed;
-
- pango_color = pango_renderer_get_color (renderer, part);
-
- if (priv->last_part != -1)
- changed = priv->gc_changed ||
- priv->stipple[priv->last_part] != priv->stipple[part] ||
- !color_equal (pango_color,
- pango_renderer_get_color (renderer, priv->last_part));
- else
- changed = TRUE;
-
- if (changed)
- {
- if (pango_color)
- {
- tmp_color.red = pango_color->red;
- tmp_color.green = pango_color->green;
- tmp_color.blue = pango_color->blue;
-
- color = &tmp_color;
- }
- else
- color = NULL;
-
- _gdk_gc_update_context (priv->base_gc,
- priv->cr,
- color,
- priv->stipple[part],
- priv->gc_changed);
- }
-
- priv->last_part = part;
- priv->gc_changed = FALSE;
- }
-
- return priv->cr;
-}
-
-static void
-gdk_pango_renderer_draw_glyphs (PangoRenderer *renderer,
- PangoFont *font,
- PangoGlyphString *glyphs,
- int x,
- int y)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
- cairo_t *cr;
-
- cr = get_cairo_context (gdk_renderer,
- PANGO_RENDER_PART_FOREGROUND);
-
- if (priv->embossed)
- {
- cairo_save (cr);
- emboss_context (cr);
- cairo_move_to (cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
- pango_cairo_show_glyph_string (cr, font, glyphs);
- cairo_restore (cr);
- }
-
- cairo_move_to (cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
- pango_cairo_show_glyph_string (cr, font, glyphs);
-}
-
-/* Draws an error underline that looks like one of:
- * H E H
- * /\ /\ /\ /\ /\ -
- * A/ \ / \ / \ A/ \ / \ |
- * \ \ / \ / /D \ \ / \ |
- * \ \/ C \/ / \ \/ C \ | height = HEIGHT_SQUARES * square
- * \ /\ F / \ F /\ \ |
- * \ / \ / \ / \ \G |
- * \ / \ / \ / \ / |
- * \/ \/ \/ \/ -
- * B B
- * |----|
- * unit_width = (HEIGHT_SQUARES - 1) * square
- *
- * The x, y, width, height passed in give the desired bounding box;
- * x/width are adjusted to make the underline a integer number of units
- * wide.
- */
-#define HEIGHT_SQUARES 2.5
-
-/* Cut-and-pasted between here and pango/pango/pangocairo-render.c */
-static void
-draw_error_underline (cairo_t *cr,
- double x,
- double y,
- double width,
- double height)
-{
- double square = height / HEIGHT_SQUARES;
- double unit_width = (HEIGHT_SQUARES - 1) * square;
- int width_units = (width + unit_width / 2) / unit_width;
- double y_top, y_bottom;
- int i;
-
- x += (width - width_units * unit_width) / 2;
- width = width_units * unit_width;
-
- y_top = y;
- y_bottom = y + height;
-
- /* Bottom of squiggle */
- cairo_move_to (cr, x - square / 2, y_top + square / 2); /* A */
- for (i = 0; i < width_units; i += 2)
- {
- double x_middle = x + (i + 1) * unit_width;
- double x_right = x + (i + 2) * unit_width;
-
- cairo_line_to (cr, x_middle, y_bottom); /* B */
-
- if (i + 1 == width_units)
- /* Nothing */;
- else if (i + 2 == width_units)
- cairo_line_to (cr, x_right + square / 2, y_top + square / 2); /* D */
- else
- cairo_line_to (cr, x_right, y_top + square); /* C */
- }
-
- /* Top of squiggle */
- for (i -= 2; i >= 0; i -= 2)
- {
- double x_left = x + i * unit_width;
- double x_middle = x + (i + 1) * unit_width;
- double x_right = x + (i + 2) * unit_width;
-
- if (i + 1 == width_units)
- cairo_line_to (cr, x_middle + square / 2, y_bottom - square / 2); /* G */
- else {
- if (i + 2 == width_units)
- cairo_line_to (cr, x_right, y_top); /* E */
- cairo_line_to (cr, x_middle, y_bottom - square); /* F */
- }
-
- cairo_line_to (cr, x_left, y_top); /* H */
- }
-
- cairo_close_path (cr);
- cairo_fill (cr);
-}
-
-static void
-gdk_pango_renderer_draw_rectangle (PangoRenderer *renderer,
- PangoRenderPart part,
- int x,
- int y,
- int width,
- int height)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
- cairo_t *cr;
-
- cr = get_cairo_context (gdk_renderer, part);
-
- if (priv->embossed && part != PANGO_RENDER_PART_BACKGROUND)
- {
- cairo_save (cr);
- emboss_context (cr);
- cairo_rectangle (cr,
- (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
- (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
-
- cairo_fill (cr);
- cairo_restore (cr);
- }
-
- cairo_rectangle (cr,
- (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
- (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
- cairo_fill (cr);
-}
-
-static void
-gdk_pango_renderer_draw_error_underline (PangoRenderer *renderer,
- int x,
- int y,
- int width,
- int height)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
- cairo_t *cr;
-
- cr = get_cairo_context (gdk_renderer, PANGO_RENDER_PART_UNDERLINE);
-
- if (priv->embossed)
- {
- cairo_save (cr);
- emboss_context (cr);
- draw_error_underline (cr,
- (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
- (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
- cairo_restore (cr);
- }
-
- draw_error_underline (cr,
- (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
- (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
-}
-
-static void
-gdk_pango_renderer_part_changed (PangoRenderer *renderer,
- PangoRenderPart part)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
-
- if (gdk_renderer->priv->last_part == part)
- gdk_renderer->priv->last_part = (PangoRenderPart)-1;
-}
-
-static void
-gdk_pango_renderer_begin (PangoRenderer *renderer)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
-
- if (!priv->drawable || !priv->base_gc)
- {
- g_warning ("gdk_pango_renderer_set_drawable() and gdk_pango_renderer_set_drawable()"
- "must be used to set the target drawable and GC before using the renderer\n");
- }
-}
-
-static void
-gdk_pango_renderer_end (PangoRenderer *renderer)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- GdkPangoRendererPrivate *priv = gdk_renderer->priv;
-
- if (priv->cr)
- {
- cairo_destroy (priv->cr);
- priv->cr = NULL;
- }
- priv->last_part = (PangoRenderPart)-1;
-}
-
-static void
-gdk_pango_renderer_prepare_run (PangoRenderer *renderer,
- PangoLayoutRun *run)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
- gboolean embossed = FALSE;
- GdkBitmap *stipple = NULL;
- GSList *l;
- int i;
-
- embossed = FALSE;
- stipple = NULL;
-
- for (l = run->item->analysis.extra_attrs; l; l = l->next)
- {
- PangoAttribute *attr = l->data;
-
- /* stipple_type and embossed_type aren't necessarily
- * initialized, but they are 0, which is an
- * invalid type so won't occur.
- */
- if (attr->klass->type == gdk_pango_attr_stipple_type)
- {
- stipple = ((GdkPangoAttrStipple*)attr)->stipple;
- }
- else if (attr->klass->type == gdk_pango_attr_embossed_type)
- {
- embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
- }
- }
-
- gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, stipple);
- gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, stipple);
- gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, stipple);
- gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, stipple);
-
- if (embossed != gdk_renderer->priv->embossed)
- {
- gdk_renderer->priv->embossed = embossed;
- pango_renderer_part_changed (renderer, PANGO_RENDER_PART_FOREGROUND);
- }
-
- PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->prepare_run (renderer, run);
-
- for (i = 0; i <= MAX_RENDER_PART; i++)
- {
- if (gdk_renderer->priv->override_color_set[i])
- pango_renderer_set_color (renderer, i, &gdk_renderer->priv->override_color[i]);
- }
-}
-
-static void
-gdk_pango_renderer_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
-
- switch (prop_id)
- {
- case PROP_SCREEN:
- gdk_renderer->priv->screen = g_value_get_object (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gdk_pango_renderer_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
-
- switch (prop_id)
- {
- case PROP_SCREEN:
- g_value_set_object (value, gdk_renderer->priv->screen);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gdk_pango_renderer_init (GdkPangoRenderer *renderer)
-{
- renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer,
- GDK_TYPE_PANGO_RENDERER,
- GdkPangoRendererPrivate);
-
- renderer->priv->last_part = (PangoRenderPart)-1;
- renderer->priv->gc_changed = TRUE;
-}
-
-static void
-gdk_pango_renderer_class_init (GdkPangoRendererClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
-
- renderer_class->draw_glyphs = gdk_pango_renderer_draw_glyphs;
- renderer_class->draw_rectangle = gdk_pango_renderer_draw_rectangle;
- renderer_class->draw_error_underline = gdk_pango_renderer_draw_error_underline;
- renderer_class->part_changed = gdk_pango_renderer_part_changed;
- renderer_class->begin = gdk_pango_renderer_begin;
- renderer_class->end = gdk_pango_renderer_end;
- renderer_class->prepare_run = gdk_pango_renderer_prepare_run;
-
- object_class->finalize = gdk_pango_renderer_finalize;
- object_class->constructor = gdk_pango_renderer_constructor;
- object_class->set_property = gdk_pango_renderer_set_property;
- object_class->get_property = gdk_pango_renderer_get_property;
-
- g_object_class_install_property (object_class,
- PROP_SCREEN,
- g_param_spec_object ("screen",
- P_("Screen"),
- P_("the GdkScreen for the renderer"),
- GDK_TYPE_SCREEN,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
- G_PARAM_STATIC_BLURB));
-
- g_type_class_add_private (object_class, sizeof (GdkPangoRendererPrivate));
-}
-
-/**
- * gdk_pango_renderer_new:
- * @screen: a #GdkScreen
- *
- * Creates a new #PangoRenderer for @screen. Normally you can use the
- * results of gdk_pango_renderer_get_default() rather than creating a new
- * renderer.
- *
- * Return value: a newly created #PangoRenderer. Free with g_object_unref().
- *
- * Since: 2.6
- **/
-PangoRenderer *
-gdk_pango_renderer_new (GdkScreen *screen)
-{
- g_return_val_if_fail (screen != NULL, NULL);
-
- return g_object_new (GDK_TYPE_PANGO_RENDERER,
- "screen", screen,
- NULL);
-}
-
-static void
-on_renderer_display_closed (GdkDisplay *display,
- gboolean is_error,
- GdkPangoRenderer *renderer)
-{
- g_signal_handlers_disconnect_by_func (display,
- on_renderer_display_closed,
- renderer);
- g_object_set_data (G_OBJECT (renderer->priv->screen),
- g_intern_static_string ("gdk-pango-renderer"), NULL);
-}
-
-/**
- * gdk_pango_renderer_get_default:
- * @screen: a #GdkScreen
- *
- * Gets the default #PangoRenderer for a screen. This default renderer
- * is shared by all users of the display, so properties such as the color
- * or transformation matrix set for the renderer may be overwritten
- * by functions such as gdk_draw_layout().
- *
- * Before using the renderer, you need to call gdk_pango_renderer_set_drawable()
- * and gdk_pango_renderer_set_gc() to set the drawable and graphics context
- * to use for drawing.
- *
- * Return value: the default #PangoRenderer for @screen. The
- * renderer is owned by GTK+ and will be kept around until the
- * screen is closed.
- *
- * Since: 2.6
- **/
-PangoRenderer *
-gdk_pango_renderer_get_default (GdkScreen *screen)
-{
- PangoRenderer *renderer;
-
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
-
- renderer = g_object_get_data (G_OBJECT (screen), "gdk-pango-renderer");
- if (!renderer)
- {
- renderer = gdk_pango_renderer_new (screen);
- g_object_set_data_full (G_OBJECT (screen),
- g_intern_static_string ("gdk-pango-renderer"), renderer,
- (GDestroyNotify)g_object_unref);