From 3d737ee8bae66d3395ff7975fafea99f87f1ed40 Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Sun, 21 Nov 2004 16:24:01 +0000 Subject: [PATCH] Add GdkPangoRenderer, a subclass of PangoRenderer targeting GDK drawables. Sat Nov 20 15:13:51 2004 Owen Taylor * gdk/gdkpango.[ch]: Add GdkPangoRenderer, a subclass of PangoRenderer targeting GDK drawables. Use to implement the old gdk_draw_layout() and friends. * gdk/gdkdraw.c gdk/gdkdrawable.h gdk/gdkwindow.c gdk/gdkpixmap.c: Add gdk_draw_glyphs_transformed() gdk_draw_trapezoids() and the corresponding members of GdkDrawableClass. Add a fallback implementation of gdk_draw_trapezoids() in terms of pixbufs. * gdk/gdkwindowing.h gdk/x11/gdkg-x11.h: Add _gdk_windowing_gc_get_foreground() to enable the fallback trapezoid implementation. * gdk/x11/gdkdrawable-x11.c gdk/x11/gdkdisplay-x11.h: Implement draw_glyph_transformed, draw_trapezoids. * gdk/x11/gdkdrawable-x11.[ch]: Add _gdk_x11_drawable_draw_xtrapezoids, _gdk_x11_drawable_draw_xft_glyphs for use of GdkX11Renderer. * gdk/x11/gdkgc-x11.c gdk/x11/gdkprivate-x11.h: Implement GDK_TILED, GDK_STIPPLED, GDK_OPAQUE_STIPPLED in the RENDER codepath. * gdk/gdkpango-x11.c: Add GdkX11Renderer... a subclass of PangoXftRenderer that does tiles/stipples and fallback rendering of trapezoids without the RENDER extension. * gdk/gdkpango-x11.c gdk/x11/gdkscreen-x11.[ch] _gdk_x11_renderer_get: Add _gdk_x11_renderer_get() to get a singleton GdkX11Renderer for the screen. * gdk/x11/gdkdrawable-x11.c (get_impl_drawable): Fix a None/NULL confusion. * gtk/gtklabel.[ch] gtk/gtk.symbols: Add gtk_label_set/get_angle(), and an ::angle property. * gtk/gtklabel.c: Remove #if 0'd dead code gtk_label_paint_word(). * gtk/gtktextdisplay.c: Switch to using a GtkTextRenderer subclass of GdkPangoRenderer for drawing. * gtk/gtktextlayout.[ch] gtk/gtktextdisplay.c: Switch to using gtk_attr_shape_new_with_data() to store backreferences to embedded pixmaps and widgets. Leave line_display->shaped_objects around for backwords compatibility. * gdk/gdkpango.[ch] (gdk_pango_context_set_colormap): Describe as deprecated, remove implementation. * gtk/gtkwidget.c (gtk_widget_create_pango_context): Remove call to gdk_pango_context_set_colormap. * demos/gtk-demo/Makefile.am demos/gtk-demo/rotated_text.c: Add a demo showing drawing rotated text. * tests/testgtk.c: Add a rotated-label test, and also a rotated drawing test (differs from demos/gtk-demo/rotated_text by also using a tile) --- demos/gtk-demo/Makefile.am | 1 + demos/gtk-demo/rotated_text.c | 142 ++++ gdk/gdk.symbols | 9 + gdk/gdkdraw.c | 356 ++++++++- gdk/gdkdrawable.h | 33 +- gdk/gdkinternals.h | 5 + gdk/gdkpango.c | 1354 ++++++++++++++++++++++----------- gdk/gdkpango.h | 76 ++ gdk/gdkpixmap.c | 53 +- gdk/gdkwindow.c | 118 ++- gdk/x11/gdkdisplay-x11.h | 3 + gdk/x11/gdkdrawable-x11.c | 193 ++++- gdk/x11/gdkdrawable-x11.h | 11 + gdk/x11/gdkgc-x11.c | 310 +++++++- gdk/x11/gdkpango-x11.c | 111 +++ gdk/x11/gdkprivate-x11.h | 8 + gdk/x11/gdkscreen-x11.c | 3 + gdk/x11/gdkscreen-x11.h | 3 + gtk/gtk.symbols | 2 + gtk/gtklabel.c | 214 +++++- gtk/gtklabel.h | 3 + gtk/gtktextdisplay.c | 817 +++++++++----------- gtk/gtktextlayout.c | 25 +- gtk/gtktextlayout.h | 2 +- tests/testgtk.c | 215 ++++++ 25 files changed, 3090 insertions(+), 977 deletions(-) create mode 100644 demos/gtk-demo/rotated_text.c diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 65a52ca9b..dd904195b 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -22,6 +22,7 @@ demos = \ menus.c \ panes.c \ pixbufs.c \ + rotated_text.c \ sizegroup.c \ stock_browser.c \ textview.c \ diff --git a/demos/gtk-demo/rotated_text.c b/demos/gtk-demo/rotated_text.c new file mode 100644 index 000000000..1c37548dd --- /dev/null +++ b/demos/gtk-demo/rotated_text.c @@ -0,0 +1,142 @@ +/* Rotated Text + * + * This demo shows how to use GDK and Pango to draw rotated and transformed + * text. The use of GdkPangoRenderer in this example is a somewhat advanced + * technique; most applications can simply use gdk_draw_layout(). We use + * it here mostly because that allows us to work in user coordinates - that is, + * coordinates prior to the application of the transformation matrix, rather + * than device coordinates. + * + * As of GTK+-2.6, the ability to draw transformed and anti-aliased graphics + * as shown in this example is only present for text. With GTK+-2.8, a new + * graphics system called "Cairo" will be introduced that provides these + * capabilities and many more for all types of graphics. + */ +#include +#include + +static GtkWidget *window = NULL; + +static gboolean +rotated_text_expose_event (GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ +#define RADIUS 150 +#define N_WORDS 10 +#define FONT "Sans Bold 27" + + PangoRenderer *renderer; + PangoMatrix matrix = PANGO_MATRIX_INIT; + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *desc; + + int width = widget->allocation.width; + int height = widget->allocation.height; + double device_radius; + int i; + + /* Get the default renderer for the screen, and set it up for drawing */ + renderer = gdk_pango_renderer_get_default (gtk_widget_get_screen (widget)); + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), widget->window); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), widget->style->black_gc); + + /* Set up a transformation matrix so that the user space coordinates for + * the centered square where we draw are [-RADIUS, RADIUS], [-RADIUS, RADIUS] + * We first center, then change the scale */ + device_radius = MIN (width, height) / 2.; + pango_matrix_translate (&matrix, + device_radius + (width - 2 * device_radius) / 2, + device_radius + (height - 2 * device_radius) / 2); + pango_matrix_scale (&matrix, device_radius / RADIUS, device_radius / RADIUS); + + /* Create a PangoLayout, set the font and text */ + context = gtk_widget_create_pango_context (widget); + 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++) + { + GdkColor color; + PangoMatrix rotated_matrix = matrix; + int width, height; + double angle = (360. * i) / N_WORDS; + + /* Gradient from red at angle == 60 to blue at angle == 300 */ + color.red = 65535 * (1 + cos ((angle - 60) * M_PI / 180.)) / 2; + color.green = 0; + color.blue = 65535 - color.red; + + gdk_pango_renderer_set_override_color (GDK_PANGO_RENDERER (renderer), + PANGO_RENDER_PART_FOREGROUND, &color); + + pango_matrix_rotate (&rotated_matrix, angle); + + pango_context_set_matrix (context, &rotated_matrix); + + /* Inform Pango to re-layout the text with the new transformation matrix */ + pango_layout_context_changed (layout); + + pango_layout_get_size (layout, &width, &height); + pango_renderer_draw_layout (renderer, layout, + - width / 2, - RADIUS * PANGO_SCALE); + } + + /* Clean up default renderer, since it is shared */ + gdk_pango_renderer_set_override_color (GDK_PANGO_RENDERER (renderer), + PANGO_RENDER_PART_FOREGROUND, NULL); + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), NULL); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), NULL); + + /* free the objects we created */ + g_object_unref (layout); + g_object_unref (context); + + return FALSE; +} + +GtkWidget * +do_rotated_text (GtkWidget *do_widget) +{ + GtkWidget *drawing_area; + + if (!window) + { + const GdkColor white = { 0, 0xffff, 0xffff, 0xffff }; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "Rotated Text"); + + g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window); + + drawing_area = gtk_drawing_area_new (); + gtk_container_add (GTK_CONTAINER (window), drawing_area); + + /* This overrides the background color from the theme */ + gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white); + + g_signal_connect (drawing_area, "expose-event", + G_CALLBACK (rotated_text_expose_event), NULL); + + gtk_window_set_default_size (GTK_WINDOW (window), 2 * RADIUS, 2 * RADIUS); + } + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_widget_show_all (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + return window; +} diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index af498c1e4..f7fc01358 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -134,6 +134,7 @@ gdk_drawable_unref gdk_draw_arc gdk_draw_drawable gdk_draw_glyphs +gdk_draw_glyphs_transformed gdk_draw_gray_image gdk_draw_image gdk_draw_indexed_image @@ -157,6 +158,7 @@ gdk_draw_segments gdk_draw_string gdk_draw_text gdk_draw_text_wc +gdk_draw_trapezoids gdk_drop_finish gdk_drop_reply gdk_error_trap_pop @@ -305,6 +307,13 @@ gdk_pango_context_get_for_screen gdk_pango_context_set_colormap gdk_pango_layout_get_clip_region gdk_pango_layout_line_get_clip_region +gdk_pango_renderer_get_default +gdk_pango_renderer_get_type +gdk_pango_renderer_new +gdk_pango_renderer_set_drawable +gdk_pango_renderer_set_gc +gdk_pango_renderer_set_override_color +gdk_pango_renderer_set_stipple gdk_parse_args gdk_pixbuf_get_from_drawable gdk_pixbuf_get_from_image diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c index 2145a7230..f547fad90 100644 --- a/gdk/gdkdraw.c +++ b/gdk/gdkdraw.c @@ -25,6 +25,7 @@ */ #include +#include #include "gdkalias.h" #include "gdkdrawable.h" #include "gdkinternals.h" @@ -58,7 +59,11 @@ static void gdk_drawable_real_draw_pixbuf (GdkDrawable *draw GdkRgbDither dither, gint x_dither, gint y_dither); - +static void gdk_drawable_real_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); + static void gdk_drawable_class_init (GdkDrawableClass *klass); GType @@ -99,6 +104,7 @@ gdk_drawable_class_init (GdkDrawableClass *klass) klass->get_clip_region = gdk_drawable_real_get_visible_region; klass->get_visible_region = gdk_drawable_real_get_visible_region; klass->draw_pixbuf = gdk_drawable_real_draw_pixbuf; + klass->draw_trapezoids = gdk_drawable_real_draw_trapezoids; } /* Manipulation of drawables @@ -873,12 +879,12 @@ gdk_draw_lines (GdkDrawable *drawable, * @font: font to be used * @x: X coordinate of baseline origin * @y: Y coordinate of baseline origin - * @glyphs: glyphs to render + * @glyphs: the glyph string to draw * * This is a low-level function; 99% of text rendering should be done * using gdk_draw_layout() instead. * - * A glyph is a character in a font. This function draws a sequence of + * A glyph is a single image in a font. This function draws a sequence of * glyphs. To obtain a sequence of glyphs you have to understand a * lot about internationalized text handling, which you don't want to * understand; thus, use gdk_draw_layout() instead of this function, @@ -900,6 +906,74 @@ gdk_draw_glyphs (GdkDrawable *drawable, GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs (drawable, gc, font, x, y, glyphs); } +/** + * gdk_draw_glyphs_transformed: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @matrix: a #PangoMatrix, or %NULL to use an identity transformation + * @font: the font in which to draw the string + * @x: the x position of the start of the string (in Pango + * units in user space coordinates) + * @y: the y position of the baseline (in Pango units + * in user space coordinates) + * @glyphs: the glyph string to draw + * + * Renders a #PangoGlyphString onto a drawable, possibly + * transforming the layed-out coordinates through a transformation + * matrix. Note that the transformation matrix for @font is not + * changed, so to produce correct rendering results, the @font + * must have been loaded using a #PangoContext with an identical + * transformation matrix to that passed in to this function. + * + * See also gdk_draw_glyphs(), gdk_draw_layout(). + * + * Since: 2.6 + **/ +void +gdk_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs) +{ + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); + g_return_if_fail (GDK_IS_GC (gc)); + + if (GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs_transformed) + GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs_transformed (drawable, gc, matrix, + font, x, y, glyphs); +} + +/** + * gdk_draw_trapezoids: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @trapezoids: an array of #GdkTrapezoid structures + * @n_trapezoids: the number of trapezoids to draw + * + * Draws a set of anti-aliased trapezoids. The trapezoids are + * combined using saturation addition, then drawn over the background + * as a set. This is low level functionality used internally to implement + * rotated underlines and backgrouds when rendering a PangoLayout and is + * likely not useful for applications. + * + * Since: 2.6 + **/ +void +gdk_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids) +{ + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); + g_return_if_fail (GDK_IS_GC (gc)); + g_return_if_fail (n_trapezoids == 0 || trapezoids != NULL); + + GDK_DRAWABLE_GET_CLASS (drawable)->draw_trapezoids (drawable, gc, + trapezoids, n_trapezoids); +} /** * gdk_drawable_copy_to_image: @@ -1534,6 +1608,282 @@ gdk_drawable_real_draw_pixbuf (GdkDrawable *drawable, g_object_unref (composited); } +/************************************************************************/ + +/* Fallback rendering code for anti-aliased trapezoids. Note that this code + * is cut-and-pasted (with the substitution of GdkPixbuf for FT_Bitmap) between + * here and pangoft2-render.c. + */ +typedef struct { + double y; + double x1; + double x2; +} Position; + +static void +draw_simple_trap (GdkPixbuf *pixbuf, + int pixbuf_x, + int pixbuf_y, + Position *t, + Position *b) +{ + guchar *pixels = gdk_pixbuf_get_pixels (pixbuf); + int rowstride = gdk_pixbuf_get_rowstride (pixbuf); + int pixbuf_width = gdk_pixbuf_get_width (pixbuf); + int pixbuf_height = gdk_pixbuf_get_height (pixbuf); + int iy = floor (t->y); + int x1, x2, x; + double dy = b->y - t->y; + guchar *dest; + + if (iy < pixbuf_y || iy >= pixbuf_y + pixbuf_height) + return; + + if (t->x1 < b->x1) + x1 = floor (t->x1); + else + x1 = floor (b->x1); + + if (t->x2 > b->x2) + x2 = ceil (t->x2); + else + x2 = ceil (b->x2); + + x1 = CLAMP (x1, pixbuf_x, pixbuf_x + pixbuf_width); + x2 = CLAMP (x2, pixbuf_x, pixbuf_x + pixbuf_width); + + dest = pixels + (iy - pixbuf_y) * rowstride + (x1 - pixbuf_x) * 4; + + for (x = x1; x < x2; x++, dest += 4) + { + double top_left = MAX (t->x1, x); + double top_right = MIN (t->x2, x + 1); + double bottom_left = MAX (b->x1, x); + double bottom_right = MIN (b->x2, x + 1); + double c = 0.5 * dy * ((top_right - top_left) + (bottom_right - bottom_left)); + + /* When converting to [0,255], we round up. This is intended + * to prevent the problem of pixels that get divided into + * multiple slices not being fully black. + */ + int ic = c * 256; + + /* We already set the entire buffer to the destination color */ + dest[3] = MIN (dest[3] + ic, 255); + } +} + +static void +interpolate_position (Position *result, + Position *top, + Position *bottom, + double val, + double val1, + double val2) +{ + result->y = (top->y * (val2 - val) + bottom->y * (val - val1)) / (val2 - val1); + result->x1 = (top->x1 * (val2 - val) + bottom->x1 * (val - val1)) / (val2 - val1); + result->x2 = (top->x2 * (val2 - val) + bottom->x2 * (val - val1)) / (val2 - val1); +} + +/* This draws a trapezoid with the parallel sides aligned with + * the X axis. We do this by subdividing the trapezoid vertically + * into thin slices (themselves trapezoids) where two edge sides are each + * contained within a single pixel and then rasterizing each + * slice. There are frequently multiple slices within a single + * line so we have to accumulate to get the final result. + */ +static void +draw_trapezoid (GdkPixbuf *pixbuf, + int pixbuf_x, + int pixbuf_y, + GdkTrapezoid *trapezoid) +{ + Position pos; + Position t; + Position b; + gboolean done = FALSE; + + if (trapezoid->y1 == trapezoid->y2) + return; + + pos.y = t.y = trapezoid->y1; + pos.x1 = t.x1 = trapezoid->x11; + pos.x2 = t.x2 = trapezoid->x21; + b.y = trapezoid->y2; + b.x1 = trapezoid->x12; + b.x2 = trapezoid->x22; + + while (!done) + { + Position pos_next; + double y_next, x1_next, x2_next; + double ix1, ix2; + + /* The algorithm here is written to emphasize simplicity and + * numerical stability as opposed to speed. + * + * While the end result is slicing up the polygon vertically, + * conceptually we aren't walking in the X direction, rather we + * are walking along the edges. When we compute crossing of + * horizontal pixel boundaries, we use the X coordinate as the + * interpolating variable, when we compute crossing for vertical + * pixel boundaries, we use the Y coordinate. + * + * This allows us to handle almost exactly horizontal edges without + * running into difficulties. (Almost exactly horizontal edges + * come up frequently due to inexactness in computing, say, + * a 90 degree rotation transformation) + */ + + pos_next = b; + done = TRUE; + + /* Check for crossing vertical pixel boundaries */ + y_next = floor (pos.y) + 1; + if (y_next < pos_next.y) + { + interpolate_position (&pos_next, &t, &b, + y_next, t.y, b.y); + pos_next.y = y_next; + done = FALSE; + } + + /* Check left side for crossing horizontal pixel boundaries */ + ix1 = floor (pos.x1); + + if (b.x1 < t.x1) + { + if (ix1 == pos.x1) + x1_next = ix1 - 1; + else + x1_next = ix1; + + if (x1_next > pos_next.x1) + { + interpolate_position (&pos_next, &t, &b, + x1_next, t.x1, b.x1); + pos_next.x1 = x1_next; + done = FALSE; + } + } + else if (b.x1 > t.x1) + { + x1_next = ix1 + 1; + + if (x1_next < pos_next.x1) + { + interpolate_position (&pos_next, &t, &b, + x1_next, t.x1, b.x1); + pos_next.x1 = x1_next; + done = FALSE; + } + } + + /* Check right side for crossing horizontal pixel boundaries */ + ix2 = floor (pos.x2); + + if (b.x2 < t.x2) + { + if (ix2 == pos.x2) + x2_next = ix2 - 1; + else + x2_next = ix2; + + if (x2_next > pos_next.x2) + { + interpolate_position (&pos_next, &t, &b, + x2_next, t.x2, b.x2); + pos_next.x2 = x2_next; + done = FALSE; + } + } + else if (trapezoid->x22 > trapezoid->x21) + { + x2_next = ix2 + 1; + + if (x2_next < pos_next.x2) + { + interpolate_position (&pos_next, &t, &b, + x2_next, t.x2, b.x2); + pos_next.x2 = x2_next; + done = FALSE; + } + } + + draw_simple_trap (pixbuf, pixbuf_x, pixbuf_y, &pos, &pos_next); + pos = pos_next; + } +} + +static void +gdk_drawable_real_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids) +{ + GdkPixbuf *pixbuf; + double min_x, max_x, min_y, max_y; + int x, y, width, height; + GdkColor color; + int i; + + if (n_trapezoids == 0) + return; + + /* compute bounding box */ + + min_x = max_x = trapezoids[0].x11; + min_y = max_y = trapezoids[0].y1; + + for (i = 0; i < n_trapezoids; i++) + { + if (trapezoids[i].x11 < min_x) min_x = trapezoids[i].x11; + if (trapezoids[i].x21 > max_x) max_x = trapezoids[i].x21; + if (trapezoids[i].x12 < min_x) min_x = trapezoids[i].x12; + if (trapezoids[i].x22 > max_x) max_x = trapezoids[i].x22; + if (trapezoids[i].y1 < min_y) min_y = trapezoids[i].y1; + if (trapezoids[i].y2 > max_y) max_y = trapezoids[i].y2; + } + + /* allocate temporary pixbuf */ + + x = floor (min_x); + width = ceil (max_x) - x; + y = floor (min_y); + height = ceil (max_y) - y; + + if (width == 0 || height == 0) + return; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + if (!pixbuf) + return; + + /* Fill the pixbuf with the foreground color and alpha 0 */ + + _gdk_windowing_gc_get_foreground (gc, &color); + gdk_pixbuf_fill (pixbuf, + (((color.red & 0xff00) << 16) | + ((color.green & 0xff00) << 8) | + ((color.blue & 0xff00)))); + + /* draw the trapezoids into the alpha channel */ + + for (i = 0; i < n_trapezoids; i++) + draw_trapezoid (pixbuf, x, y, &trapezoids[i]); + + /* composite that onto the drawable */ + + gdk_draw_pixbuf (drawable, gc, pixbuf, + 0, 0, x, y, width, height, + GDK_RGB_DITHER_NORMAL, 0, 0); + + g_object_unref (pixbuf); +} + +/************************************************************************/ + /** * _gdk_drawable_get_scratch_gc: * @drawable: A #GdkDrawable diff --git a/gdk/gdkdrawable.h b/gdk/gdkdrawable.h index 7f8058493..cc2cf6a4b 100644 --- a/gdk/gdkdrawable.h +++ b/gdk/gdkdrawable.h @@ -11,6 +11,7 @@ extern "C" { #endif /* __cplusplus */ typedef struct _GdkDrawableClass GdkDrawableClass; +typedef struct _GdkTrapezoid GdkTrapezoid; #define GDK_TYPE_DRAWABLE (gdk_drawable_get_type ()) #define GDK_DRAWABLE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DRAWABLE, GdkDrawable)) @@ -154,10 +155,20 @@ struct _GdkDrawableClass gint dest_y, gint width, gint height); + + void (*draw_glyphs_transformed) (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); + void (*draw_trapezoids) (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); /* Padding for future expansion */ - void (*_gdk_reserved1) (void); - void (*_gdk_reserved2) (void); void (*_gdk_reserved3) (void); void (*_gdk_reserved4) (void); void (*_gdk_reserved5) (void); @@ -173,6 +184,11 @@ struct _GdkDrawableClass void (*_gdk_reserved16) (void); }; +struct _GdkTrapezoid +{ + double y1, x11, x21, y2, x12, x22; +}; + GType gdk_drawable_get_type (void); /* Manipulation of drawables @@ -338,6 +354,18 @@ void gdk_draw_layout_with_colors (GdkDrawable *drawable, const GdkColor *foreground, const GdkColor *background); +void gdk_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); +void gdk_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); + #ifndef GDK_DISABLE_DEPRECATED #define gdk_draw_pixmap gdk_draw_drawable #define gdk_draw_bitmap gdk_draw_drawable @@ -360,7 +388,6 @@ GdkImage *gdk_drawable_copy_to_image (GdkDrawable *drawable, GdkRegion *gdk_drawable_get_clip_region (GdkDrawable *drawable); GdkRegion *gdk_drawable_get_visible_region (GdkDrawable *drawable); - gboolean gdk_draw_rectangle_alpha_libgtk_only (GdkDrawable *drawable, gint x, gint y, diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index b9e14eba4..19667da3b 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -320,6 +320,11 @@ void _gdk_windowing_display_set_sm_client_id (GdkDisplay *display, GType _gdk_window_impl_get_type (void) G_GNUC_CONST; GType _gdk_pixmap_impl_get_type (void) G_GNUC_CONST; + +/* Queries the current foreground color of a GdkGC */ +void _gdk_windowing_gc_get_foreground (GdkGC *gc, + GdkColor *color); + /************************************ * Initialization and exit routines * ************************************/ diff --git a/gdk/gdkpango.c b/gdk/gdkpango.c index 93e5ab288..374d90d6d 100644 --- a/gdk/gdkpango.c +++ b/gdk/gdkpango.c @@ -18,6 +18,7 @@ */ #include +#include #include "gdkalias.h" #include "gdkcolor.h" #include "gdkgc.h" @@ -26,195 +27,870 @@ #include "gdkprivate.h" #include "gdkscreen.h" +/* This is for P_() ... a bit non-kosher, but works fine */ +#include "gtk/gtkintl.h" + #define GDK_INFO_KEY "gdk-info" -typedef struct _GdkPangoContextInfo GdkPangoContextInfo; +/* 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 _GdkPangoContextInfo +struct _GdkPangoRendererPrivate { - GdkColormap *colormap; + 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; + + /* When switching between the normal and shadow copies when + * drawing shadows we can get unexpected recursion into the + * drawing functions; the 'in_emboss' flag guards against that. + */ + gboolean in_emboss; + + /* Current target */ + GdkDrawable *drawable; + GdkGC *base_gc; + + /* Cached GC, derived from base_gc */ + GdkGC *gc; + PangoColor gc_color; + gboolean gc_color_set; + GdkBitmap *gc_stipple; + + /* we accumulate trapezoids for the same PangoRenderPart */ + GArray *trapezoids; + PangoRenderPart trapezoid_part; }; static PangoAttrType gdk_pango_attr_stipple_type; static PangoAttrType gdk_pango_attr_embossed_type; -static void gdk_pango_get_item_properties (PangoItem *item, - PangoUnderline *uline, - gboolean *strikethrough, - gint *rise, - PangoColor *fg_color, - gboolean *fg_set, - PangoColor *bg_color, - gboolean *bg_set, - gboolean *embossed, - GdkBitmap **stipple, - gboolean *shape_set, - PangoRectangle *ink_rect, - PangoRectangle *logical_rect); +static void flush_trapezoids (GdkPangoRenderer *gdk_renderer); + +enum { + PROP_0, + PROP_SCREEN +}; + +G_DEFINE_TYPE (GdkPangoRenderer, gdk_pango_renderer, PANGO_TYPE_RENDERER) static void -gdk_pango_context_destroy (GdkPangoContextInfo *info) +gdk_pango_renderer_finalize (GObject *object) { - if (info->colormap) - g_object_unref (info->colormap); - g_free (info); + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object); + GdkPangoRendererPrivate *priv = gdk_renderer->priv; + int i; + + if (priv->gc) + g_object_unref (priv->gc); + if (priv->gc_stipple) + g_object_unref (priv->gc_stipple); + 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_array_free (priv->trapezoids, TRUE); + + G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->finalize (object); } -static GdkPangoContextInfo * -gdk_pango_context_get_info (PangoContext *context, gboolean create) +static GObject* +gdk_pango_renderer_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) { - GdkPangoContextInfo *info = - g_object_get_qdata (G_OBJECT (context), - g_quark_try_string (GDK_INFO_KEY)); - if (!info && create) + 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) { - info = g_new (GdkPangoContextInfo, 1); - info->colormap = NULL; + g_warning ("Screen must be specified at construct time for GdkPangoRenderer"); + gdk_renderer->priv->screen = gdk_screen_get_default (); + } - g_object_set_qdata_full (G_OBJECT (context), - g_quark_from_static_string (GDK_INFO_KEY), - info, (GDestroyNotify)gdk_pango_context_destroy); + return object; +} + +/* Adjusts matrix and color for the renderer to draw the secondar + * "shadow" copy for embossed text */ +static void +emboss_renderer (PangoRenderer *renderer, + PangoRenderPart part, + PangoMatrix **save_matrix, + PangoColor **save_color) +{ + GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv; + static const PangoColor white = { 0xffff, 0xffff, 0xffff }; + PangoMatrix tmp_matrix = PANGO_MATRIX_INIT; + + priv->in_emboss = TRUE; + + *save_color = pango_renderer_get_color (renderer, part); + if (*save_color) + *save_color = pango_color_copy (*save_color); + + *save_matrix = renderer->matrix; + if (*save_matrix) + { + *save_matrix = pango_matrix_copy (*save_matrix); + tmp_matrix = **save_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 + */ + tmp_matrix.x0 += 1; + tmp_matrix.y0 += 1; + + pango_renderer_set_matrix (renderer, &tmp_matrix); + pango_renderer_set_color (renderer, part, &white); +} - return info; +/* Restores from emboss_renderer() */ +static void +unemboss_renderer (PangoRenderer *renderer, + PangoRenderPart part, + PangoMatrix **save_matrix, + PangoColor **save_color) +{ + GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv; + pango_renderer_set_matrix (renderer, *save_matrix); + pango_renderer_set_color (renderer, part, *save_color); + + if (*save_matrix) + pango_matrix_free (*save_matrix); + if (*save_color) + pango_color_free (*save_color); + + priv->in_emboss = FALSE; } +/* Gets the GC for drawing @part. This make involve copying the base GC + * for the renderer, in which case we keep a one-GC cache. */ static GdkGC * -gdk_pango_get_gc (GdkDrawable *drawable, - PangoContext *context, - PangoColor *fg_color, - GdkBitmap *stipple, - GdkGC *base_gc) +get_gc (GdkPangoRenderer *gdk_renderer, + PangoRenderPart part) +{ + PangoRenderer *renderer = PANGO_RENDERER (gdk_renderer); + PangoColor *color; + GdkBitmap *stipple; + GdkPangoRendererPrivate *priv = gdk_renderer->priv; + + color = pango_renderer_get_color (renderer, part); + + if (part <= MAX_RENDER_PART) + stipple = priv->stipple[part]; + else + stipple = NULL; + + if (!color && !stipple) /* nothing override, use base_gc */ + return priv->base_gc; + else + { + gboolean new_stipple = FALSE; + gboolean new_color = FALSE; + + if (stipple != priv->gc_stipple) + new_stipple = TRUE; + + if ((priv->gc_color_set && !color) || + (!priv->gc_color_set && color) || + priv->gc_color.red != color->red || + priv->gc_color.green != color->green || + priv->gc_color.blue != color->blue) + new_color = TRUE; + + if (!priv->gc) + { + priv->gc = gdk_gc_new (priv->drawable); + gdk_gc_copy (priv->gc, priv->base_gc); + } + else if (new_color && priv->gc_color_set && !color) + { + /* We have to recopy the original GC onto the cached GC + * to get the default color */ + new_stipple = TRUE; + gdk_gc_copy (priv->gc, priv->base_gc); + } + else if (new_stipple && priv->gc_stipple && !stipple) + { + /* Similarly, we need to make a new copy to restore to the + * default stipple state (the caller may have set a stipple + * on the GC, and even if not, gdk_gc_set_stipple (gc, NULL) + * doesn't work currently to restore to the default X stipple) */ + new_color = TRUE; + gdk_gc_copy (priv->gc, priv->base_gc); + } + + if (new_color) + { + if (color) + { + GdkColor gdk_color; + + gdk_color.red = color->red; + gdk_color.green = color->green; + gdk_color.blue = color->blue; + + gdk_gc_set_rgb_fg_color (priv->gc, &gdk_color); + + priv->gc_color = *color; + priv->gc_color_set = TRUE; + } + else + priv->gc_color_set = FALSE; + } + + if (new_stipple) + { + if (priv->gc_stipple) + g_object_unref (priv->gc_stipple); + + if (stipple) + { + gdk_gc_set_stipple (priv->gc, stipple); + gdk_gc_set_fill (priv->gc, GDK_STIPPLED); + priv->gc_stipple = g_object_ref (stipple); + } + else + priv->gc_stipple = NULL; + } + + return priv->gc; + } +} + +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; + + flush_trapezoids (gdk_renderer); + + if (!priv->in_emboss && priv->embossed) + { + PangoMatrix *save_matrix; + PangoColor *save_color; + + emboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color); + gdk_draw_glyphs_transformed (priv->drawable, + get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND), + renderer->matrix, font, x, y, glyphs); + unemboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color); + } + + gdk_draw_glyphs_transformed (priv->drawable, + get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND), + renderer->matrix, font, x, y, glyphs); +} + +/* Outputs any pending trapezoids, we do this when the part or + * part color changes, when we are about to draw text, etc. */ +static void +flush_trapezoids (GdkPangoRenderer *gdk_renderer) { - GdkGC *result; - GdkPangoContextInfo *info; + GdkPangoRendererPrivate *priv = gdk_renderer->priv; + + if (!priv->trapezoids || priv->trapezoids->len == 0) + return; + + gdk_draw_trapezoids (priv->drawable, + get_gc (gdk_renderer, priv->trapezoid_part), + (GdkTrapezoid *)priv->trapezoids->data, + priv->trapezoids->len); + + g_array_set_size (priv->trapezoids, 0); +} + +/* Draws a single trapezoid ... we don't draw it immediately, but rather + * cache it to join together with other trapezoids that form part of the + * same logical shape */ +static void +gdk_pango_renderer_draw_trapezoid (PangoRenderer *renderer, + PangoRenderPart part, + double y1, + double x11, + double x21, + double y2, + double x12, + double x22) +{ + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + GdkTrapezoid trap; + + if (!gdk_renderer->priv->trapezoids) + gdk_renderer->priv->trapezoids = g_array_new (FALSE, FALSE, + sizeof (GdkTrapezoid)); - g_return_val_if_fail (context != NULL, NULL); + if (gdk_renderer->priv->trapezoids->len > 0 && + gdk_renderer->priv->trapezoid_part != part) + flush_trapezoids (gdk_renderer); + + gdk_renderer->priv->trapezoid_part = part; + + trap.y1 = y1; + trap.x11 = x11; + trap.x21 = x21; + trap.y2 = y2; + trap.x12 = x12; + trap.x22 = x22; - info = gdk_pango_context_get_info (context, FALSE); + g_array_append_val (gdk_renderer->priv->trapezoids, trap); +} + +/* We can't handle embossing at the level of trapezoids, because when an + * underline is split into multiple trapezoids, the normal and shadow + * trapezoids will be drawn mixed together. Instead, we have to emboss + * and entire rectangle or error underline + */ + +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; - if (info == NULL || info->colormap == NULL) + if (!priv->in_emboss && priv->embossed && part != PANGO_RENDER_PART_BACKGROUND) { - g_warning ("you must set the colormap on a PangoContext before using it to draw a layout"); - return NULL; + PangoMatrix *save_matrix; + PangoColor *save_color; + + emboss_renderer (renderer, part, &save_matrix, &save_color); + PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part, + x, y, width, height); + unemboss_renderer (renderer, part, &save_matrix, &save_color); + } + + PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part, + x, y, width, height); +} + +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; + + if (!priv->in_emboss && priv->embossed) + { + PangoMatrix *save_matrix; + PangoColor *save_color; + + emboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color); + PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer, + x, y, width, height); + unemboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color); + } + + PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer, + x, y, width, height); +} + +static void +gdk_pango_renderer_part_changed (PangoRenderer *renderer, + PangoRenderPart part) +{ + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + + if (part == gdk_renderer->priv->trapezoid_part) + flush_trapezoids (gdk_renderer); +} + +static void +gdk_pango_renderer_begin (PangoRenderer *renderer) +{ + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + + if (!gdk_renderer->priv->drawable || !gdk_renderer->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); + + flush_trapezoids (gdk_renderer); +} - result = gdk_gc_new (drawable); - gdk_gc_copy (result, base_gc); +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; - if (fg_color) + for (l = run->item->analysis.extra_attrs; l; l = l->next) { - GdkColor color; - - color.red = fg_color->red; - color.green = fg_color->green; - color.blue = fg_color->blue; + PangoAttribute *attr = l->data; - gdk_rgb_find_color (info->colormap, &color); - gdk_gc_set_foreground (result, &color); + /* 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; + } } - if (stipple) + 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_gc_set_fill (result, GDK_STIPPLED); - gdk_gc_set_stipple (result, stipple); + 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); +} + +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_trapezoid = gdk_pango_renderer_draw_trapezoid; + 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_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 result; + return g_object_new (GDK_TYPE_PANGO_RENDERER, + "screen", screen, + NULL); } static void -gdk_pango_free_gc (PangoContext *context, - GdkGC *gc) +on_renderer_display_closed (GdkDisplay *display, + GdkPangoRenderer *renderer) { - g_object_unref (gc); + g_signal_handlers_disconnect_by_func (renderer->priv->screen, + (gpointer)on_renderer_display_closed, + renderer); + g_object_set_data (G_OBJECT (renderer->priv->screen), "gdk-pango-renderer", NULL); } /** - * gdk_pango_context_set_colormap: - * @context: a #PangoContext - * @colormap: a #GdkColormap + * 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(). * - * Sets the colormap to be used for drawing with @context. + * Before using the renderer, you need to call gdk_pango_renderer_set_drawable() + * and gdk_pango_renderer_set_drawable() 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. * - * If you obtained your context from gtk_widget_get_pango_context() or - * gtk_widget_create_pango_context(), the colormap will already be set - * to the colormap for the widget, so you shouldn't need this - * function. + * 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), "gdk-pango-renderer", renderer, + (GDestroyNotify)g_object_unref); + + g_signal_connect (gdk_screen_get_display (screen), "closed", + G_CALLBACK (on_renderer_display_closed), renderer); + } + + return renderer; +} + +/** + * gdk_pango_renderer_set_drawable: + * @gdk_renderer: a #GdkPangoRenderer + * @drawable: the new target drawable, or %NULL + * + * Sets the drawable the renderer draws to. + * + * Since: 2.6 **/ void -gdk_pango_context_set_colormap (PangoContext *context, - GdkColormap *colormap) +gdk_pango_renderer_set_drawable (GdkPangoRenderer *gdk_renderer, + GdkDrawable *drawable) +{ + GdkPangoRendererPrivate *priv; + + g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer)); + g_return_if_fail (drawable == NULL || GDK_IS_DRAWABLE (drawable)); + + priv = gdk_renderer->priv; + + flush_trapezoids (gdk_renderer); + + if (priv->drawable != drawable) + { + if (priv->drawable) + g_object_unref (priv->drawable); + priv->drawable = drawable; + if (priv->drawable) + g_object_ref (priv->drawable); + } +} + +/** + * gdk_pango_renderer_set_gc: + * @gdk_renderer: a #GdkPangoRenderer + * @gc: the new GC to use for drawing, or %NULL + * + * Sets the GC the renderer draws with. Note that the GC must not be + * modified until it is unset by calling the function again with + * %NULL for the @gc parameter, since GDK may make internal copies + * of the GC which won't be updated to follow changes to the + * original GC. + * + * Since: 2.6 + **/ +void +gdk_pango_renderer_set_gc (GdkPangoRenderer *gdk_renderer, + GdkGC *gc) { - GdkPangoContextInfo *info; + GdkPangoRendererPrivate *priv; - g_return_if_fail (context != NULL); + g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer)); + g_return_if_fail (gc == NULL || GDK_IS_GC (gc)); - info = gdk_pango_context_get_info (context, TRUE); - g_return_if_fail (info != NULL); + priv = gdk_renderer->priv; - if (info->colormap != colormap) + flush_trapezoids (gdk_renderer); + + if (priv->base_gc != gc) { - if (info->colormap) - g_object_unref (info->colormap); + if (priv->base_gc) + g_object_unref (priv->base_gc); + priv->base_gc = gc; + if (priv->base_gc) + g_object_ref (priv->base_gc); - info->colormap = colormap; + if (priv->gc) + { + g_object_unref (priv->gc); + priv->gc = NULL; + } - if (info->colormap) - g_object_ref (info->colormap); + priv->gc_color_set = FALSE; + + if (priv->gc_stipple) + { + g_object_unref (priv->gc_stipple); + priv->gc_stipple = NULL; + } } } -static void -draw_underline (GdkDrawable *drawable, - GdkGC *gc, - PangoUnderline uline, - int baseline_y, - int low_y, - int start_x, - int end_x) -{ - switch (uline) + +/** + * gdk_pango_renderer_set_stipple: + * @gdk_renderer: a #GdkPangoRenderer + * @part: the part to render with the stipple + * @stipple: the new stipple value. + * + * Sets the stipple for one render part (foreground, background, underline, + * etc.) Note that this is overwritten when iterating through the individual + * styled runs of a #PangoLayout or #PangoLayoutLine. This function is thus + * only useful when you call low level functions like pango_renderer_draw_glyphs() + * directly, or in the 'prepare_run' virtual function of a subclass of + * #GdkPangoRenderer. + * + * Since: 2.6 + **/ +void +gdk_pango_renderer_set_stipple (GdkPangoRenderer *gdk_renderer, + PangoRenderPart part, + GdkBitmap *stipple) +{ + g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer)); + + if (part > MAX_RENDER_PART) /* Silently ignore unknown parts */ + return; + + if (stipple != gdk_renderer->priv->stipple[part]) { - case PANGO_UNDERLINE_NONE: - break; - case PANGO_UNDERLINE_DOUBLE: - gdk_draw_line (drawable, gc, - start_x, baseline_y + 3, - end_x, baseline_y + 3); - /* Fall through */ - case PANGO_UNDERLINE_SINGLE: - gdk_draw_line (drawable, gc, - start_x, baseline_y + 1, - end_x, baseline_y + 1); - break; - case PANGO_UNDERLINE_ERROR: - { - int point_x, point_y; - int counter = 0; - - for (point_x = start_x; - point_x <= end_x; - point_x += 2) - { - point_y = counter ? baseline_y + 1 : baseline_y + 2; - - gdk_draw_line (drawable, gc, - point_x, point_y, - MIN (point_x + 1, end_x), point_y); - - counter = (counter + 1) % 2; - } - } - break; - case PANGO_UNDERLINE_LOW: - gdk_draw_line (drawable, gc, - start_x, low_y + 1, - end_x, low_y + 1); - break; + if (gdk_renderer->priv->stipple[part]) + g_object_unref (gdk_renderer->priv->stipple[part]); + + gdk_renderer->priv->stipple[part] = stipple; + + if (gdk_renderer->priv->stipple[part]) + g_object_ref (gdk_renderer->priv->stipple[part]); + + pango_renderer_part_changed (PANGO_RENDERER (gdk_renderer), part); } } +/** + * gdk_pango_renderer_set_override_color: + * @gdk_renderer: a #GdkPangoRenderer + * @part: the part to render to set the color of + * @color: the color to use, or %NULL to unset a previously + * set override color. + * + * Sets the color for a particular render part (foreground, + * background, underline, etc.), overriding any attributes on the layouts + * renderered with this renderer. + * + * Since: 2.6 + **/ +void +gdk_pango_renderer_set_override_color (GdkPangoRenderer *gdk_renderer, + PangoRenderPart part, + const GdkColor *color) +{ + GdkPangoRendererPrivate *priv; + + g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer)); + + priv = gdk_renderer->priv; + + if (part > MAX_RENDER_PART) /* Silently ignore unknown parts */ + return; + + if (color) + { + priv->override_color[part].red = color->red; + priv->override_color[part].green = color->green; + priv->override_color[part].blue = color->blue; + priv->override_color_set[part] = TRUE; + } + else + priv->override_color_set[part] = FALSE; +} + +/** + * gdk_pango_context_set_colormap: + * @context: a #PangoContext + * @colormap: a #GdkColormap + * + * This function used to set the colormap to be used for drawing with + * @context. The colormap is now always derived from the graphics + * context used for drawing, so calling this function is no longer + * necessary. + **/ +void +gdk_pango_context_set_colormap (PangoContext *context, + GdkColormap *colormap) +{ + g_return_if_fail (PANGO_IS_CONTEXT (context)); + g_return_if_fail (colormap == NULL || GDK_IS_COLORMAP (colormap)); +} + +/* Gets a renderer to draw with, setting the properties of the + * renderer and activating it. Note that since we activate the + * renderer here, the implicit setting of the matrix that + * pango_renderer_draw_layout_[line] normally do when they + * activate the renderer is suppressed. */ +static PangoRenderer * +get_renderer (GdkDrawable *drawable, + GdkGC *gc, + const GdkColor *foreground, + const GdkColor *background) +{ + GdkScreen *screen = gdk_drawable_get_screen (drawable); + PangoRenderer *renderer = gdk_pango_renderer_get_default (screen); + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + + gdk_pango_renderer_set_drawable (gdk_renderer, drawable); + gdk_pango_renderer_set_gc (gdk_renderer, gc); + + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_FOREGROUND, + foreground); + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_UNDERLINE, + foreground); + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_STRIKETHROUGH, + foreground); + + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_BACKGROUND, + background); + + pango_renderer_activate (renderer); + + return renderer; +} + +/* Cleans up the renderer obtained with get_renderer() */ +static void +release_renderer (PangoRenderer *renderer) +{ + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + + pango_renderer_deactivate (renderer); + + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_FOREGROUND, + NULL); + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_UNDERLINE, + NULL); + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_STRIKETHROUGH, + NULL); + gdk_pango_renderer_set_override_color (gdk_renderer, + PANGO_RENDER_PART_BACKGROUND, + NULL); + + gdk_pango_renderer_set_drawable (gdk_renderer, NULL); + gdk_pango_renderer_set_gc (gdk_renderer, NULL); +} + /** * gdk_draw_layout_line_with_colors: * @drawable: the drawable on which to draw the line @@ -228,6 +904,11 @@ draw_underline (GdkDrawable *drawable, * Render a #PangoLayoutLine onto a #GdkDrawable, overriding the * layout's normal colors with @foreground and/or @background. * @foreground and @background need not be allocated. + * + * If the layout's #PangoContext has a transformation matrix set, then + * @x and @y specify the position of the left edge of the baseline + * (left is in before-tranform user coordinates) in after-transform + * device coordinates. */ void gdk_draw_layout_line_with_colors (GdkDrawable *drawable, @@ -238,215 +919,88 @@ gdk_draw_layout_line_with_colors (GdkDrawable *drawable, const GdkColor *foreground, const GdkColor *background) { - GSList *tmp_list = line->runs; - PangoRectangle overall_rect; - PangoRectangle logical_rect; - PangoRectangle ink_rect; - PangoContext *context; - gint x_off = 0; - gint rise = 0; - gboolean embossed; - GdkBitmap *stipple; - PangoUnderline last_uline = PANGO_UNDERLINE_NONE; - gint uline_start_x = 0; - gint uline_end_x = 0; - gint uline_end_x_extended = 0; - gint last_risen_y = 0; - gint low_y = G_MININT; - GdkGC *last_fg_gc = NULL; - gboolean last_fg_set = FALSE; - PangoColor last_fg_color; - + PangoRenderer *renderer; + const PangoMatrix *matrix; + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); g_return_if_fail (GDK_IS_GC (gc)); g_return_if_fail (line != NULL); - context = pango_layout_get_context (line->layout); - - pango_layout_line_get_extents (line,NULL, &overall_rect); - - while (tmp_list) + renderer = get_renderer (drawable, gc, foreground, background); + + /* When we have a matrix, we do positioning by adjusting the matrix, and + * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce + * a matrix when the caller didn't provide one, however, since that adds + * lots of floating point arithmetic for each glyph. + */ + matrix = pango_context_get_matrix (pango_layout_get_context (line->layout)); + if (matrix) { - PangoUnderline this_uline = PANGO_UNDERLINE_NONE; - PangoLayoutRun *run = tmp_list->data; - PangoColor fg_color, bg_color; - gboolean strike, fg_set, bg_set, shape_set; - GdkGC *fg_gc; - gint risen_y; - - tmp_list = tmp_list->next; + PangoMatrix tmp_matrix; - gdk_pango_get_item_properties (run->item, &this_uline, - &strike, - &rise, - &fg_color, &fg_set, - &bg_color, &bg_set, - &embossed, - &stipple, - &shape_set, - &ink_rect, - &logical_rect); - - /* we subtract the rise because X coordinates are upside down */ - risen_y = y - rise / PANGO_SCALE; - - if (!shape_set) - { - if (this_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); - } + tmp_matrix = *matrix; + tmp_matrix.x0 = x; + tmp_matrix.y0 = y; + pango_renderer_set_matrix (renderer, &tmp_matrix); - if (bg_set || background) - { - GdkGC *bg_gc; - PangoColor tmp = bg_color; - - if (background) - { - tmp.red = background->red; - tmp.blue = background->blue; - tmp.green = background->green; - } - - bg_gc = gdk_pango_get_gc (drawable, context, &tmp, stipple, gc); - - gdk_draw_rectangle (drawable, bg_gc, TRUE, - x + (x_off + logical_rect.x) / PANGO_SCALE, - risen_y + overall_rect.y / PANGO_SCALE, - logical_rect.width / PANGO_SCALE, - overall_rect.height / PANGO_SCALE); - - if (stipple) - gdk_gc_set_fill (bg_gc, GDK_SOLID); - - gdk_pango_free_gc (context, bg_gc); - } + x = 0; + y = 0; + } + else + pango_renderer_set_matrix (renderer, NULL); - if (fg_set || stipple || foreground) - { - PangoColor tmp = fg_color; - - if (foreground) - { - tmp.red = foreground->red; - tmp.blue = foreground->blue; - tmp.green = foreground->green; - } - - fg_gc = gdk_pango_get_gc (drawable, context, (fg_set || foreground) ? &tmp : NULL, - stipple, gc); - } - else - fg_gc = gc; + pango_renderer_draw_layout_line (renderer, line, x * PANGO_SCALE, y * PANGO_SCALE); - if (!shape_set) - { - gint gx, gy; + release_renderer (renderer); +} - gx = x + x_off / PANGO_SCALE; - gy = risen_y; - - if (embossed) - { - PangoColor color = { 65535, 65535, 65535 }; - GdkGC *white_gc = gdk_pango_get_gc (drawable, context, &color, stipple, fg_gc); - gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font, - gx + 1, - gy + 1, - run->glyphs); - gdk_pango_free_gc (context, white_gc); - } - - gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font, - gx, gy, - run->glyphs); - } +/* Gets the bounds of a layout in device coordinates. Note cut-and-paste + * between here and gtklabel.c */ +static void +get_rotated_layout_bounds (PangoLayout *layout, + GdkRectangle *rect) +{ + PangoContext *context = pango_layout_get_context (layout); + const PangoMatrix *matrix = pango_context_get_matrix (context); + gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */ + PangoRectangle logical_rect; + gint i, j; - if (this_uline != last_uline || - risen_y != last_risen_y || - fg_set != last_fg_set || - (fg_set && (last_fg_color.red != fg_color.red || - last_fg_color.green != fg_color.green || - last_fg_color.blue != fg_color.blue))) + pango_layout_get_extents (layout, NULL, &logical_rect); + + for (i = 0; i < 2; i++) + { + gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width; + for (j = 0; j < 2; j++) { - /* If only color changes, the underlines extend to the edge - * of the logical rectangle so they join up; otherwise they - * go 1 pixel beyond the ink rectangle. This doesn't work - * for low underlines (they will be at a different y anyways), - * so they follow the normal path. - */ - gboolean extend_uline = (this_uline == last_uline && - this_uline != PANGO_UNDERLINE_LOW && - risen_y == last_risen_y); + gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height; + + gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0; + gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0; - /* Starting a new underline run - */ - if (last_uline != PANGO_UNDERLINE_NONE) + if (i == 0 && j == 0) { - draw_underline (drawable, last_fg_gc, last_uline, - last_risen_y, low_y, - uline_start_x, - extend_uline ? uline_end_x_extended : uline_end_x); + x_min = x_max = xt; + y_min = y_max = yt; } - - if (this_uline != PANGO_UNDERLINE_NONE) + else { - if (extend_uline) - uline_start_x = x + x_off / PANGO_SCALE; - else - uline_start_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1; - - low_y = G_MININT; + if (xt < x_min) + x_min = xt; + if (yt < y_min) + y_min = yt; + if (xt > x_max) + x_max = xt; + if (yt > y_max) + y_max = yt; } } - - /* Update current underline segment information - */ - if (this_uline != PANGO_UNDERLINE_NONE) - { - uline_end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE; - uline_end_x_extended = x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE - 1; - } - - if (this_uline == PANGO_UNDERLINE_LOW) - low_y = MAX (low_y, risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE); - - if (strike) - { - int centerline = logical_rect.y + logical_rect.height / 2; - - gdk_draw_line (drawable, fg_gc, - x + (x_off + logical_rect.x) / PANGO_SCALE - 1, - risen_y + centerline / PANGO_SCALE, - x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1, - risen_y + centerline / PANGO_SCALE); - } - - if (last_fg_gc != gc && last_fg_gc) - gdk_pango_free_gc (context, last_fg_gc); - - last_risen_y = risen_y; - last_uline = this_uline; - last_fg_gc = fg_gc; - last_fg_set = fg_set; - if (fg_set) - last_fg_color = fg_color; - - x_off += logical_rect.width; } - - /* Finish off any remaining underlines - */ - if (last_uline != PANGO_UNDERLINE_NONE) - draw_underline (drawable, last_fg_gc, last_uline, last_risen_y, low_y, - uline_start_x, uline_end_x); - - if (last_fg_gc != gc && last_fg_gc) - gdk_pango_free_gc (context, last_fg_gc); + + rect->x = floor (x_min); + rect->width = ceil (x_max) - rect->x; + rect->y = floor (y_min); + rect->height = floor (y_max) - rect->y; } /** @@ -463,6 +1017,10 @@ gdk_draw_layout_line_with_colors (GdkDrawable *drawable, * layout's normal colors with @foreground and/or @background. * @foreground and @background need not be allocated. * + * If the layout's #PangoContext has a transformation matrix set, then + * @x and @y specify the position of the top left corner of the + * bounding box (in device space) of the transformed layout. + * * If you're using GTK+, the ususal way to obtain a #PangoLayout * is gtk_widget_create_pango_layout(). */ @@ -475,35 +1033,42 @@ gdk_draw_layout_with_colors (GdkDrawable *drawable, const GdkColor *foreground, const GdkColor *background) { - PangoLayoutIter *iter; + PangoRenderer *renderer; + const PangoMatrix *matrix; g_return_if_fail (GDK_IS_DRAWABLE (drawable)); g_return_if_fail (GDK_IS_GC (gc)); g_return_if_fail (PANGO_IS_LAYOUT (layout)); - iter = pango_layout_get_iter (layout); - - do + renderer = get_renderer (drawable, gc, foreground, background); + + /* When we have a matrix, we do positioning by adjusting the matrix, and + * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce + * a matrix when the caller didn't provide one, however, since that adds + * lots of floating point arithmetic for each glyph. + */ + matrix = pango_context_get_matrix (pango_layout_get_context (layout)); + if (matrix) { - PangoRectangle logical_rect; - PangoLayoutLine *line; - int baseline; - - line = pango_layout_iter_get_line (iter); + PangoMatrix tmp_matrix; + GdkRectangle rect; + + get_rotated_layout_bounds (layout, &rect); - pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); - baseline = pango_layout_iter_get_baseline (iter); + tmp_matrix = *matrix; + tmp_matrix.x0 += x - rect.x; + tmp_matrix.y0 += y - rect.y; + pango_renderer_set_matrix (renderer, &tmp_matrix); - gdk_draw_layout_line_with_colors (drawable, gc, - x + logical_rect.x / PANGO_SCALE, - y + baseline / PANGO_SCALE, - line, - foreground, - background); + x = 0; + y = 0; } - while (pango_layout_iter_next_line (iter)); - - pango_layout_iter_free (iter); + else + pango_renderer_set_matrix (renderer, NULL); + + pango_renderer_draw_layout (renderer, layout, x * PANGO_SCALE, y * PANGO_SCALE); + + release_renderer (renderer); } /** @@ -515,6 +1080,11 @@ gdk_draw_layout_with_colors (GdkDrawable *drawable, * @line: a #PangoLayoutLine * * Render a #PangoLayoutLine onto an GDK drawable + * + * If the layout's #PangoContext has a transformation matrix set, then + * @x and @y specify the position of the left edge of the baseline + * (left is in before-tranform user coordinates) in after-transform + * device coordinates. */ void gdk_draw_layout_line (GdkDrawable *drawable, @@ -540,7 +1110,11 @@ gdk_draw_layout_line (GdkDrawable *drawable, * * Render a #PangoLayout onto a GDK drawable * - * If you're using GTK+, the ususal way to obtain a #PangoLayout + * If the layout's #PangoContext has a transformation matrix set, then + * @x and @y specify the position of the top left corner of the + * bounding box (in device space) of the transformed layout. + * + * If you're using GTK+, the usual way to obtain a #PangoLayout * is gtk_widget_create_pango_layout(). */ void @@ -557,110 +1131,6 @@ gdk_draw_layout (GdkDrawable *drawable, gdk_draw_layout_with_colors (drawable, gc, x, y, layout, NULL, NULL); } -static void -gdk_pango_get_item_properties (PangoItem *item, - PangoUnderline *uline, - gboolean *strikethrough, - gint *rise, - PangoColor *fg_color, - gboolean *fg_set, - PangoColor *bg_color, - gboolean *bg_set, - gboolean *embossed, - GdkBitmap **stipple, - gboolean *shape_set, - PangoRectangle *ink_rect, - PangoRectangle *logical_rect) -{ - GSList *tmp_list = item->analysis.extra_attrs; - - if (strikethrough) - *strikethrough = FALSE; - - if (fg_set) - *fg_set = FALSE; - - if (bg_set) - *bg_set = FALSE; - - if (shape_set) - *shape_set = FALSE; - - if (rise) - *rise = 0; - - if (embossed) - *embossed = FALSE; - - if (stipple) - *stipple = NULL; - - while (tmp_list) - { - PangoAttribute *attr = tmp_list->data; - - switch (attr->klass->type) - { - case PANGO_ATTR_UNDERLINE: - if (uline) - *uline = ((PangoAttrInt *)attr)->value; - break; - - case PANGO_ATTR_STRIKETHROUGH: - if (strikethrough) - *strikethrough = ((PangoAttrInt *)attr)->value; - break; - - case PANGO_ATTR_FOREGROUND: - if (fg_color) - *fg_color = ((PangoAttrColor *)attr)->color; - if (fg_set) - *fg_set = TRUE; - - break; - - case PANGO_ATTR_BACKGROUND: - if (bg_color) - *bg_color = ((PangoAttrColor *)attr)->color; - if (bg_set) - *bg_set = TRUE; - - break; - - case PANGO_ATTR_SHAPE: - if (shape_set) - *shape_set = TRUE; - if (logical_rect) - *logical_rect = ((PangoAttrShape *)attr)->logical_rect; - if (ink_rect) - *ink_rect = ((PangoAttrShape *)attr)->ink_rect; - break; - - case PANGO_ATTR_RISE: - if (rise) - *rise = ((PangoAttrInt *)attr)->value; - break; - - default: - /* stipple_type and embossed_type aren't necessarily - * initialized, but they are 0, which is an - * invalid type so won't occur. - */ - if (stipple && attr->klass->type == gdk_pango_attr_stipple_type) - { - *stipple = ((GdkPangoAttrStipple*)attr)->stipple; - } - else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type) - { - *embossed = ((GdkPangoAttrEmbossed*)attr)->embossed; - } - break; - } - tmp_list = tmp_list->next; - } -} - - static PangoAttribute * gdk_pango_attr_stipple_copy (const PangoAttribute *attr) { diff --git a/gdk/gdkpango.h b/gdk/gdkpango.h index 5a35c45fb..492f144ad 100644 --- a/gdk/gdkpango.h +++ b/gdk/gdkpango.h @@ -28,12 +28,88 @@ extern "C" { /* Pango interaction */ +typedef struct _GdkPangoRenderer GdkPangoRenderer; +typedef struct _GdkPangoRendererClass GdkPangoRendererClass; +typedef struct _GdkPangoRendererPrivate GdkPangoRendererPrivate; + +#define GDK_TYPE_PANGO_RENDERER (gdk_pango_renderer_get_type()) +#define GDK_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PANGO_RENDERER, GdkPangoRenderer)) +#define GDK_IS_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PANGO_RENDERER)) +#define GDK_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PANGO_RENDERER, GdkPangoRendererClass)) +#define GDK_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PANGO_RENDERER)) +#define GDK_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PANGO_RENDERER, GdkPangoRendererClass)) + +/** + * GdkPangoRenderer: + * + * #GdkPangoRenderer is a subclass of #PangoRenderer used for rendering + * Pango objects into GDK drawables. The default renderer for a particular + * screen is obtained with gdk_pango_renderer_get_default(); Pango + * functions like pango_renderer_draw_layout() and + * pango_renderer_draw_layout_line() are then used to draw objects with + * the renderer. + * + * In most simple cases, applications can just use gdk_draw_layout(), and + * don't need to directly use #GdkPangoRenderer at all. Using the + * #GdkPangoRenderer directly is most useful when working with a + * transformation such as a rotation, because the Pango drawing functions + * take user space coordinates (coordinates before the transformation) + * instead of device coordinates. + * + * In certain cases it can be useful to subclass #GdkPangoRenderer. Examples + * of reasons to do this are to add handling of custom attributes by + * overriding 'prepare_run' or to do custom drawing of embedded objects + * by overriding 'draw_shape'. + * + * Since: 2.6 + **/ +struct _GdkPangoRenderer +{ + /*< private >*/ + PangoRenderer parent_instance; + + GdkPangoRendererPrivate *priv; +}; + +/** + * GdkPangoRendererClass: + * + * #GdkPangoRenderer is the class structure for #GdkPangoRenderer. + * + * Since: 2.6 + **/ +struct _GdkPangoRendererClass +{ + /*< private >*/ + PangoRendererClass parent_class; +}; + +GType gdk_pango_renderer_get_type (void) G_GNUC_CONST; + +PangoRenderer *gdk_pango_renderer_new (GdkScreen *screen); +PangoRenderer *gdk_pango_renderer_get_default (GdkScreen *screen); + +void gdk_pango_renderer_set_drawable (GdkPangoRenderer *gdk_renderer, + GdkDrawable *drawable); +void gdk_pango_renderer_set_gc (GdkPangoRenderer *gdk_renderer, + GdkGC *gc); +void gdk_pango_renderer_set_stipple (GdkPangoRenderer *gdk_renderer, + PangoRenderPart part, + GdkBitmap *stipple); +void gdk_pango_renderer_set_override_color (GdkPangoRenderer *gdk_renderer, + PangoRenderPart part, + const GdkColor *color); + +/************************************************************************/ + PangoContext *gdk_pango_context_get_for_screen (GdkScreen *screen); #ifndef GDK_MULTIHEAD_SAFE PangoContext *gdk_pango_context_get (void); #endif +#ifndef GDK_DISABLE_DEPRECATED void gdk_pango_context_set_colormap (PangoContext *context, GdkColormap *colormap); +#endif /* Get a clip region to draw only part of a layout or diff --git a/gdk/gdkpixmap.c b/gdk/gdkpixmap.c index c4b9c55fd..978712a9e 100644 --- a/gdk/gdkpixmap.c +++ b/gdk/gdkpixmap.c @@ -90,12 +90,21 @@ static void gdk_pixmap_draw_lines (GdkDrawable *drawable, GdkGC *gc, GdkPoint *points, gint npoints); -static void gdk_pixmap_draw_glyphs (GdkDrawable *drawable, - GdkGC *gc, - PangoFont *font, - gint x, - gint y, - PangoGlyphString *glyphs); + +static void gdk_pixmap_draw_glyphs (GdkDrawable *drawable, + GdkGC *gc, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); +static void gdk_pixmap_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); + static void gdk_pixmap_draw_image (GdkDrawable *drawable, GdkGC *gc, GdkImage *image, @@ -117,7 +126,10 @@ static void gdk_pixmap_draw_pixbuf (GdkDrawable *drawable, GdkRgbDither dither, gint x_dither, gint y_dither); - +static void gdk_pixmap_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); static void gdk_pixmap_real_get_size (GdkDrawable *drawable, gint *width, @@ -201,8 +213,10 @@ gdk_pixmap_class_init (GdkPixmapObjectClass *klass) drawable_class->draw_segments = gdk_pixmap_draw_segments; drawable_class->draw_lines = gdk_pixmap_draw_lines; drawable_class->draw_glyphs = gdk_pixmap_draw_glyphs; + drawable_class->draw_glyphs_transformed = gdk_pixmap_draw_glyphs_transformed; drawable_class->draw_image = gdk_pixmap_draw_image; drawable_class->draw_pixbuf = gdk_pixmap_draw_pixbuf; + drawable_class->draw_trapezoids = gdk_pixmap_draw_trapezoids; drawable_class->get_depth = gdk_pixmap_real_get_depth; drawable_class->get_screen = gdk_pixmap_real_get_screen; drawable_class->get_size = gdk_pixmap_real_get_size; @@ -371,6 +385,20 @@ gdk_pixmap_draw_glyphs (GdkDrawable *drawable, gdk_draw_glyphs (private->impl, gc, font, x, y, glyphs); } +static void +gdk_pixmap_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs) +{ + GdkPixmapObject *private = (GdkPixmapObject *)drawable; + + gdk_draw_glyphs_transformed (private->impl, gc, matrix, font, x, y, glyphs); +} + static void gdk_pixmap_draw_image (GdkDrawable *drawable, GdkGC *gc, @@ -409,6 +437,17 @@ gdk_pixmap_draw_pixbuf (GdkDrawable *drawable, dither, x_dither, y_dither); } +static void +gdk_pixmap_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids) +{ + GdkPixmapObject *private = (GdkPixmapObject *)drawable; + + gdk_draw_trapezoids (private->impl, gc, trapezoids, n_trapezoids); +} + static void gdk_pixmap_real_get_size (GdkDrawable *drawable, gint *width, diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 556d1c9da..06212913b 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -105,12 +105,20 @@ static void gdk_window_draw_lines (GdkDrawable *drawable, GdkGC *gc, GdkPoint *points, gint npoints); -static void gdk_window_draw_glyphs (GdkDrawable *drawable, - GdkGC *gc, - PangoFont *font, - gint x, - gint y, - PangoGlyphString *glyphs); + +static void gdk_window_draw_glyphs (GdkDrawable *drawable, + GdkGC *gc, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); +static void gdk_window_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); static void gdk_window_draw_image (GdkDrawable *drawable, GdkGC *gc, @@ -135,6 +143,11 @@ static void gdk_window_draw_pixbuf (GdkDrawable *drawable, gint x_dither, gint y_dither); +static void gdk_window_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); + static GdkImage* gdk_window_copy_to_image (GdkDrawable *drawable, GdkImage *image, gint src_x, @@ -239,8 +252,10 @@ gdk_window_class_init (GdkWindowObjectClass *klass) drawable_class->draw_segments = gdk_window_draw_segments; drawable_class->draw_lines = gdk_window_draw_lines; drawable_class->draw_glyphs = gdk_window_draw_glyphs; + drawable_class->draw_glyphs_transformed = gdk_window_draw_glyphs_transformed; drawable_class->draw_image = gdk_window_draw_image; drawable_class->draw_pixbuf = gdk_window_draw_pixbuf; + drawable_class->draw_trapezoids = gdk_window_draw_trapezoids; drawable_class->get_depth = gdk_window_real_get_depth; drawable_class->get_screen = gdk_window_real_get_screen; drawable_class->get_size = gdk_window_real_get_size; @@ -1656,6 +1671,51 @@ gdk_window_draw_glyphs (GdkDrawable *drawable, RESTORE_GC (gc); } +static void +gdk_window_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs) +{ + GdkWindowObject *private = (GdkWindowObject *)drawable; + PangoMatrix tmp_matrix; + + OFFSET_GC (gc); + + if (GDK_WINDOW_DESTROYED (drawable)) + return; + + if (x_offset != 0 || y_offset != 0) + { + if (matrix) + { + tmp_matrix = *matrix; + tmp_matrix.x0 -= x_offset; + tmp_matrix.y0 -= y_offset; + matrix = &tmp_matrix; + } + else + { + x -= x_offset * PANGO_SCALE; + y -= y_offset * PANGO_SCALE; + } + } + + if (private->paint_stack) + { + GdkWindowPaint *paint = private->paint_stack->data; + + gdk_draw_glyphs_transformed (paint->pixmap, gc, matrix, font, x, y, glyphs); + } + else + gdk_draw_glyphs_transformed (private->impl, gc, matrix, font, x, y, glyphs); + + RESTORE_GC (gc); +} + static GdkGC * gdk_window_get_bg_gc (GdkWindow *window, GdkWindowPaint *paint) @@ -1895,6 +1955,52 @@ gdk_window_draw_pixbuf (GdkDrawable *drawable, } } +static void +gdk_window_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids) +{ + GdkWindowObject *private = (GdkWindowObject *)drawable; + GdkTrapezoid *new_trapezoids = NULL; + + OFFSET_GC (gc); + + if (GDK_WINDOW_DESTROYED (drawable)) + return; + + if (x_offset != 0 || y_offset != 0) + { + gint i; + + new_trapezoids = g_new (GdkTrapezoid, n_trapezoids); + for (i=0; i < n_trapezoids; i++) + { + new_trapezoids[i].y1 = trapezoids[i].y1 - y_offset; + new_trapezoids[i].x11 = trapezoids[i].x11 - x_offset; + new_trapezoids[i].x21 = trapezoids[i].x21 - x_offset; + new_trapezoids[i].y2 = trapezoids[i].y2 - y_offset; + new_trapezoids[i].x12 = trapezoids[i].x12 - x_offset; + new_trapezoids[i].x22 = trapezoids[i].x22 - x_offset; + } + + trapezoids = new_trapezoids; + } + + if (private->paint_stack) + { + GdkWindowPaint *paint = private->paint_stack->data; + gdk_draw_trapezoids (paint->pixmap, gc, trapezoids, n_trapezoids); + } + else + gdk_draw_trapezoids (private->impl, gc, trapezoids, n_trapezoids); + + if (new_trapezoids) + g_free (new_trapezoids); + + RESTORE_GC (gc); +} + static void gdk_window_real_get_size (GdkDrawable *drawable, gint *width, diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h index a523da940..198bf6a72 100644 --- a/gdk/x11/gdkdisplay-x11.h +++ b/gdk/x11/gdkdisplay-x11.h @@ -148,6 +148,9 @@ struct _GdkDisplayX11 guint xdnd_atoms_precached : 1; guint motif_atoms_precached : 1; guint use_sync : 1; + + /* Alpha mask picture format */ + XRenderPictFormat *mask_format; }; struct _GdkDisplayX11Class diff --git a/gdk/x11/gdkdrawable-x11.c b/gdk/x11/gdkdrawable-x11.c index 2308a3acd..6517f36a5 100644 --- a/gdk/x11/gdkdrawable-x11.c +++ b/gdk/x11/gdkdrawable-x11.c @@ -105,12 +105,21 @@ static void gdk_x11_draw_lines (GdkDrawable *drawable, GdkGC *gc, GdkPoint *points, gint npoints); -static void gdk_x11_draw_glyphs (GdkDrawable *drawable, - GdkGC *gc, - PangoFont *font, - gint x, - gint y, - PangoGlyphString *glyphs); + +static void gdk_x11_draw_glyphs (GdkDrawable *drawable, + GdkGC *gc, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); +static void gdk_x11_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs); + static void gdk_x11_draw_image (GdkDrawable *drawable, GdkGC *gc, GdkImage *image, @@ -133,6 +142,11 @@ static void gdk_x11_draw_pixbuf (GdkDrawable *drawable, gint x_dither, gint y_dither); +static void gdk_x11_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids); + static void gdk_x11_set_colormap (GdkDrawable *drawable, GdkColormap *colormap); @@ -196,8 +210,10 @@ gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass) drawable_class->draw_segments = gdk_x11_draw_segments; drawable_class->draw_lines = gdk_x11_draw_lines; drawable_class->draw_glyphs = gdk_x11_draw_glyphs; + drawable_class->draw_glyphs_transformed = gdk_x11_draw_glyphs_transformed; drawable_class->draw_image = gdk_x11_draw_image; drawable_class->draw_pixbuf = gdk_x11_draw_pixbuf; + drawable_class->draw_trapezoids = gdk_x11_draw_trapezoids; drawable_class->set_colormap = gdk_x11_set_colormap; drawable_class->get_colormap = gdk_x11_get_colormap; @@ -351,7 +367,7 @@ gdk_x11_drawable_get_picture (GdkDrawable *drawable) static void gdk_x11_drawable_update_xft_clip (GdkDrawable *drawable, - GdkGC *gc) + GdkGC *gc) { GdkGCX11 *gc_private = gc ? GDK_GC_X11 (gc) : NULL; XftDraw *xft_draw = gdk_x11_drawable_get_xft_draw (drawable); @@ -789,21 +805,43 @@ gdk_x11_draw_glyphs (GdkDrawable *drawable, gint x, gint y, PangoGlyphString *glyphs) +{ + gdk_x11_draw_glyphs_transformed (drawable, gc, NULL, + font, + x * PANGO_SCALE, + y * PANGO_SCALE, + glyphs); +} + +static void +gdk_x11_draw_glyphs_transformed (GdkDrawable *drawable, + GdkGC *gc, + PangoMatrix *matrix, + PangoFont *font, + gint x, + gint y, + PangoGlyphString *glyphs) { GdkDrawableImplX11 *impl; + PangoRenderer *renderer; XftColor color; XftDraw *draw; impl = GDK_DRAWABLE_IMPL_X11 (drawable); g_return_if_fail (PANGO_XFT_IS_FONT (font)); - + _gdk_gc_x11_get_fg_xft_color (gc, &color); gdk_x11_drawable_update_xft_clip (drawable, gc); draw = gdk_x11_drawable_get_xft_draw (drawable); - pango_xft_render (draw, &color, font, glyphs, x, y); + renderer = _gdk_x11_renderer_get (drawable, gc); + if (matrix) + pango_renderer_set_matrix (renderer, matrix); + pango_renderer_draw_glyphs (renderer, font, glyphs, x, y); + if (matrix) + pango_renderer_set_matrix (renderer, NULL); } static void @@ -841,24 +879,20 @@ gdk_x11_get_depth (GdkDrawable *drawable) return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper); } - -static GdkDrawable * get_impl_drawable (GdkDrawable *drawable) +static GdkDrawable * +get_impl_drawable (GdkDrawable *drawable) { - GdkDrawable *impl; - if (GDK_IS_WINDOW (drawable)) - impl = ((GdkWindowObject *)drawable)->impl; + return ((GdkWindowObject *)drawable)->impl; else if (GDK_IS_PIXMAP (drawable)) - impl = ((GdkPixmapObject *)drawable)->impl; + return ((GdkPixmapObject *)drawable)->impl; else { g_warning (G_STRLOC " drawable is not a pixmap or window"); - return (GdkDrawable *)None; + return NULL; } - return impl; } - static GdkScreen* gdk_x11_get_screen (GdkDrawable *drawable) { @@ -1458,6 +1492,47 @@ gdk_x11_draw_pixbuf (GdkDrawable *drawable, dest_x, dest_y, width, height); } +static void +gdk_x11_draw_trapezoids (GdkDrawable *drawable, + GdkGC *gc, + GdkTrapezoid *trapezoids, + gint n_trapezoids) +{ + GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen; + GdkDisplay *display = gdk_screen_get_display (screen); + XTrapezoid *xtrapezoids; + gint i; + + if (!_gdk_x11_have_render (display)) + { + GdkDrawable *wrapper = GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper; + GDK_DRAWABLE_CLASS (parent_class)->draw_trapezoids (wrapper, gc, + trapezoids, n_trapezoids); + return; + } + + xtrapezoids = g_new (XTrapezoid, n_trapezoids); + + for (i = 0; i < n_trapezoids; i++) + { + xtrapezoids[i].top = XDoubleToFixed (trapezoids[i].y1); + xtrapezoids[i].bottom = XDoubleToFixed (trapezoids[i].y2); + xtrapezoids[i].left.p1.x = XDoubleToFixed (trapezoids[i].x11); + xtrapezoids[i].left.p1.y = XDoubleToFixed (trapezoids[i].y1); + xtrapezoids[i].left.p2.x = XDoubleToFixed (trapezoids[i].x12); + xtrapezoids[i].left.p2.y = XDoubleToFixed (trapezoids[i].y2); + xtrapezoids[i].right.p1.x = XDoubleToFixed (trapezoids[i].x21); + xtrapezoids[i].right.p1.y = XDoubleToFixed (trapezoids[i].y1); + xtrapezoids[i].right.p2.x = XDoubleToFixed (trapezoids[i].x22); + xtrapezoids[i].right.p2.y = XDoubleToFixed (trapezoids[i].y2); + } + + _gdk_x11_drawable_draw_xtrapezoids (drawable, gc, + xtrapezoids, n_trapezoids); + + g_free (xtrapezoids); +} + /** * gdk_draw_rectangle_alpha_libgtk_only: * @drawable: The #GdkDrawable to draw on @@ -1522,3 +1597,85 @@ gdk_draw_rectangle_alpha_libgtk_only (GdkDrawable *drawable, width, height); return TRUE; } + +void +_gdk_x11_drawable_draw_xtrapezoids (GdkDrawable *drawable, + GdkGC *gc, + XTrapezoid *xtrapezoids, + int n_trapezoids) +{ + GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen; + GdkDisplay *display = gdk_screen_get_display (screen); + GdkDisplayX11 *x11display = GDK_DISPLAY_X11 (display); + + XftDraw *draw; + + if (!_gdk_x11_have_render (display)) + { + /* This is the case of drawing the borders of the unknown glyph box + * without render on the display, we need to feed it back to + * fallback code. Not efficient, but doesn't matter. + */ + GdkTrapezoid *trapezoids = g_new (GdkTrapezoid, n_trapezoids); + int i; + + for (i = 0; i < n_trapezoids; i++) + { + trapezoids[i].y1 = XFixedToDouble (xtrapezoids[i].top); + trapezoids[i].y2 = XFixedToDouble (xtrapezoids[i].bottom); + trapezoids[i].x11 = XFixedToDouble (xtrapezoids[i].left.p1.x); + trapezoids[i].x12 = XFixedToDouble (xtrapezoids[i].left.p2.x); + trapezoids[i].x21 = XFixedToDouble (xtrapezoids[i].right.p1.x); + trapezoids[i].x22 = XFixedToDouble (xtrapezoids[i].right.p2.x); + } + + gdk_x11_draw_trapezoids (drawable, gc, trapezoids, n_trapezoids); + g_free (trapezoids); + + return; + } + + draw = gdk_x11_drawable_get_xft_draw (drawable); + + if (!x11display->mask_format) + x11display->mask_format = XRenderFindStandardFormat (x11display->xdisplay, + PictStandardA8); + + XRenderCompositeTrapezoids (x11display->xdisplay, PictOpOver, + _gdk_x11_gc_get_fg_picture (gc), + XftDrawPicture (draw), + x11display->mask_format, + - gc->ts_x_origin, - gc->ts_y_origin, + xtrapezoids, n_trapezoids); +} + +void +_gdk_x11_drawable_draw_xft_glyphs (GdkDrawable *drawable, + GdkGC *gc, + XftFont *xft_font, + XftGlyphSpec *glyphs, + gint n_glyphs) +{ + GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen; + GdkDisplay *display = gdk_screen_get_display (screen); + GdkDisplayX11 *x11display = GDK_DISPLAY_X11 (display); + + XftDraw *draw = gdk_x11_drawable_get_xft_draw (drawable); + + if (_gdk_x11_have_render (display)) + { + XftGlyphSpecRender (x11display->xdisplay, PictOpOver, + _gdk_x11_gc_get_fg_picture (gc), + xft_font, + XftDrawPicture (draw), + - gc->ts_x_origin, - gc->ts_y_origin, + glyphs, n_glyphs); + } + else + { + XftColor color; + + _gdk_gc_x11_get_fg_xft_color (gc, &color); + XftDrawGlyphSpec (draw, &color, xft_font, glyphs, n_glyphs); + } +} diff --git a/gdk/x11/gdkdrawable-x11.h b/gdk/x11/gdkdrawable-x11.h index bc3f75dbc..45c8e38b8 100644 --- a/gdk/x11/gdkdrawable-x11.h +++ b/gdk/x11/gdkdrawable-x11.h @@ -90,6 +90,17 @@ void _gdk_x11_convert_to_format (guchar *src_buf, gint width, gint height); +/* Note that the following take GdkDrawableImplX11, not the wrapper drawable */ +void _gdk_x11_drawable_draw_xtrapezoids (GdkDrawable *drawable, + GdkGC *gc, + XTrapezoid *xtrapezoids, + int n_trapezoids); +void _gdk_x11_drawable_draw_xft_glyphs (GdkDrawable *drawable, + GdkGC *gc, + XftFont *xft_font, + XftGlyphSpec *glyphs, + gint n_glyphs); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gdk/x11/gdkgc-x11.c b/gdk/x11/gdkgc-x11.c index fec9784df..391c3deec 100644 --- a/gdk/x11/gdkgc-x11.c +++ b/gdk/x11/gdkgc-x11.c @@ -112,6 +112,11 @@ gdk_gc_x11_finalize (GObject *object) if (x11_gc->fg_picture != None) XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), x11_gc->fg_picture); + + if (x11_gc->stipple) + g_object_unref (x11_gc->stipple); + if (x11_gc->tile) + g_object_unref (x11_gc->tile); XFreeGC (GDK_GC_XDISPLAY (x11_gc), GDK_GC_XGC (x11_gc)); @@ -161,6 +166,26 @@ _gdk_x11_gc_new (GdkDrawable *drawable, if (values_mask & GDK_GC_FOREGROUND) private->fg_pixel = values->foreground.pixel; + if (values_mask & GDK_GC_BACKGROUND) + private->bg_pixel = values->background.pixel; + + if (values_mask & GDK_GC_FILL) + private->fill = values->fill; + + if (values_mask & GDK_GC_STIPPLE) + { + private->stipple = values->stipple; + if (private->stipple) + g_object_ref (private->stipple); + } + + if (values_mask & GDK_GC_TILE) + { + private->tile = values->tile; + if (private->tile) + g_object_ref (private->tile); + } + if ((values_mask & GDK_GC_CLIP_MASK) && values->clip_mask) private->have_clip_mask = TRUE; @@ -368,6 +393,17 @@ gdk_x11_gc_get_values (GdkGC *gc, } } +static void +clear_fg_picture (GdkGC *gc) +{ + GdkGCX11 *x11_gc = GDK_GC_X11 (gc); + + if (x11_gc->fg_picture != None) + { + XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), x11_gc->fg_picture); + x11_gc->fg_picture = None; + } +} static void gdk_x11_gc_set_values (GdkGC *gc, @@ -405,6 +441,53 @@ gdk_x11_gc_set_values (GdkGC *gc, if (values_mask & GDK_GC_FOREGROUND) x11_gc->fg_pixel = values->foreground.pixel; + + if (values_mask & GDK_GC_BACKGROUND) + { + if (x11_gc->bg_pixel != values->background.pixel) + { + x11_gc->bg_pixel = values->background.pixel; + if (x11_gc->fill == GDK_OPAQUE_STIPPLED) + clear_fg_picture (gc); + } + } + + if (values_mask & GDK_GC_FILL) + { + if (x11_gc->fill != values->fill) + { + clear_fg_picture (gc); + x11_gc->fill = values->fill; + } + } + + if (values_mask & GDK_GC_STIPPLE) + { + if (x11_gc->stipple != values->stipple) + { + if (x11_gc->fill == GDK_STIPPLED || x11_gc->fill == GDK_OPAQUE_STIPPLED) + clear_fg_picture (gc); + if (x11_gc->stipple) + g_object_unref (x11_gc->stipple); + x11_gc->stipple = values->stipple; + if (x11_gc->stipple) + g_object_ref (x11_gc->stipple); + } + } + + if (values_mask & GDK_GC_TILE) + { + if (x11_gc->tile != values->tile) + { + if (x11_gc->fill == GDK_TILED) + clear_fg_picture (gc); + if (x11_gc->tile) + g_object_unref (x11_gc->tile); + x11_gc->tile = values->tile; + if (x11_gc->tile) + g_object_ref (x11_gc->tile); + } + } gdk_x11_gc_values_to_xvalues (values, values_mask, &xvalues, &xvalues_mask); @@ -784,6 +867,21 @@ gdk_gc_copy (GdkGC *dst_gc, GdkGC *src_gc) x11_dst_gc->dirty_mask = x11_src_gc->dirty_mask; x11_dst_gc->fg_pixel = x11_src_gc->fg_pixel; + x11_dst_gc->fill = x11_src_gc->fill; + + if (x11_dst_gc->stipple) + g_object_unref (x11_dst_gc->stipple); + x11_dst_gc->stipple = x11_src_gc->stipple; + if (x11_dst_gc->stipple) + g_object_ref (x11_dst_gc->stipple); + + if (x11_dst_gc->tile) + g_object_unref (x11_dst_gc->tile); + x11_dst_gc->tile = x11_src_gc->tile; + if (x11_dst_gc->tile) + g_object_ref (x11_dst_gc->tile); + + clear_fg_picture (dst_gc); } /** @@ -870,14 +968,118 @@ foreground_format (GdkGC *gc) 0); } +static Picture +make_fg_tile_picture (GdkGC *gc) +{ + GdkGCX11 *x11_gc = GDK_GC_X11 (gc); + GdkVisual *visual = gdk_drawable_get_visual (x11_gc->tile); + XRenderPictFormat *format = NULL; + + if (visual) + { + format = XRenderFindVisualFormat (GDK_GC_XDISPLAY (gc), + GDK_VISUAL_XVISUAL (visual)); + } + else if (x11_gc->depth == 1) + { + format = XRenderFindStandardFormat (GDK_GC_XDISPLAY (gc), + PictStandardA1); + } + + if (format) + { + XRenderPictureAttributes pa; + pa.repeat = True; + + return XRenderCreatePicture (GDK_GC_XDISPLAY (gc), + GDK_PIXMAP_XID (x11_gc->tile), + format, + CPRepeat, &pa); + } +} + +static Picture +make_stipple_picture (GdkGC *gc) +{ + GdkGCX11 *x11_gc = GDK_GC_X11 (gc); + XRenderPictFormat *format = NULL; + XRenderPictureAttributes pa; + + format = XRenderFindStandardFormat (GDK_GC_XDISPLAY (gc), + PictStandardA1); + + pa.repeat = True; + return XRenderCreatePicture (GDK_GC_XDISPLAY (gc), + GDK_PIXMAP_XID (x11_gc->stipple), + format, + CPRepeat, &pa); +} + +static Picture +make_color_picture (GdkGC *gc, + XRenderColor *color) +{ + GdkGCX11 *x11_gc = GDK_GC_X11 (gc); + XRenderPictureAttributes pa; + XRenderPictFormat *pix_format = foreground_format (gc); + Pixmap pix; + Picture picture; + + if (!pix_format) + return None; + + pix = XCreatePixmap (GDK_GC_XDISPLAY (gc), + GDK_SCREEN_XROOTWIN (x11_gc->screen), + 1, 1, pix_format->depth); + pa.repeat = True; + picture = XRenderCreatePicture (GDK_GC_XDISPLAY (gc), + pix, + pix_format, + CPRepeat, &pa); + XFreePixmap (GDK_GC_XDISPLAY (gc), pix); + + XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, + picture, color, + 0, 0, 1, 1); + + return picture; +} + +static void +get_bg_color (GdkGC *gc, + XRenderColor *render_color) +{ + GdkGCX11 *x11_gc = GDK_GC_X11 (gc); + GdkColormap *cmap; + + cmap = gdk_gc_get_colormap (gc); + + if (cmap) + { + GdkColor color; + + gdk_colormap_query_color (cmap, x11_gc->bg_pixel, &color); + + render_color->alpha = 0xffff; + render_color->red = color.red; + render_color->green = color.green; + render_color->blue = color.blue; + } + else /* Not worth warning, just use black */ + { + render_color->alpha = 0xffff; + render_color->red = 0; + render_color->green = 0; + render_color->blue = 0; + } +} + /** * _gdk_x11_gc_get_fg_picture: * @gc: a #GdkGC * * Gets a Xrender Picture object suitable for being the source * drawable for drawing with the foreground the graphics context. - * (Currently, only foreground color is handled, but in the - * future we should handle tiles/stipples as well.) * * Return value: a Picture, owned by the GC; this cannot be * used over subsequent modification of the GC. @@ -888,6 +1090,8 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc) GdkGCX11 *x11_gc; gboolean new = FALSE; XftColor xftcolor; + GdkFill fill; + int width, height; g_return_val_if_fail (GDK_IS_GC_X11 (gc), None); @@ -896,6 +1100,34 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc) x11_gc = GDK_GC_X11 (gc); + fill = GDK_SOLID; + width = 1; + height = 1; + + switch (x11_gc->fill) + { + case GDK_SOLID: + break; + case GDK_TILED: + if (x11_gc->tile) + { + if (!x11_gc->fg_picture) + x11_gc->fg_picture = make_fg_tile_picture (gc); + + if (x11_gc->fg_picture != None) + return x11_gc->fg_picture; + } + break; + case GDK_STIPPLED: + case GDK_OPAQUE_STIPPLED: + if (x11_gc->stipple) + { + gdk_drawable_get_size (x11_gc->stipple, &width, &height); + fill = x11_gc->fill; + } + break; + } + if (x11_gc->fg_picture == None) { XRenderPictureAttributes pa; @@ -907,7 +1139,7 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc) pix = XCreatePixmap (GDK_GC_XDISPLAY (gc), GDK_SCREEN_XROOTWIN (x11_gc->screen), - 1, 1, pix_format->depth); + width, height, pix_format->depth); pa.repeat = True; x11_gc->fg_picture = XRenderCreatePicture (GDK_GC_XDISPLAY (gc), pix, @@ -920,18 +1152,65 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc) _gdk_gc_x11_get_fg_xft_color (gc, &xftcolor); - if (new || + if (x11_gc->fg_picture_color.alpha != 0xffff || x11_gc->fg_picture_color.red != xftcolor.color.red || x11_gc->fg_picture_color.green != xftcolor.color.green || x11_gc->fg_picture_color.blue != xftcolor.color.blue) { + x11_gc->fg_picture_color.alpha = 0xffff; x11_gc->fg_picture_color.red = xftcolor.color.red; x11_gc->fg_picture_color.green = xftcolor.color.green; x11_gc->fg_picture_color.blue = xftcolor.color.blue; + new = TRUE; + } + + switch (fill) + { + case GDK_SOLID: XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, x11_gc->fg_picture, &x11_gc->fg_picture_color, - 0, 0, 1, 1); + 0, 0, width, height); + break; + case GDK_STIPPLED: + { + Picture stipple_picture = make_stipple_picture (gc); + + XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, + x11_gc->fg_picture, &x11_gc->fg_picture_color, + 0, 0, width, height); + XRenderComposite (GDK_GC_XDISPLAY (gc), + PictOpInReverse, + stipple_picture, None, x11_gc->fg_picture, + 0, 0, 0, 0, 0, 0, width, height); + + XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), stipple_picture); + } + break; + case GDK_OPAQUE_STIPPLED: + { + XRenderColor bg_color; + + Picture stipple_picture = make_stipple_picture (gc); + Picture fg_picture = make_color_picture (gc, &x11_gc->fg_picture_color); + + get_bg_color (gc, &bg_color); + + XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, + x11_gc->fg_picture, &bg_color, + 0, 0, width, height); + XRenderComposite (GDK_GC_XDISPLAY (gc), + PictOpOver, + fg_picture, stipple_picture, x11_gc->fg_picture, + 0, 0, 0, 0, 0, 0, width, height); + + XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), stipple_picture); + XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), fg_picture); + } + break; + case GDK_TILED: + g_assert_not_reached (); /* handled above */ + break; } return x11_gc->fg_picture; @@ -998,3 +1277,24 @@ _gdk_gc_x11_get_fg_xft_color (GdkGC *gc, "gdk_gc_set_colormap"); } } + +void +_gdk_windowing_gc_get_foreground (GdkGC *gc, + GdkColor *color) +{ + GdkGCX11 *x11_gc; + GdkColormap *cmap; + + g_return_if_fail (GDK_IS_GC_X11 (gc)); + + x11_gc = GDK_GC_X11 (gc); + + color->pixel = x11_gc->fg_pixel; + + cmap = gdk_gc_get_colormap (gc); + + if (cmap) + gdk_colormap_query_color (cmap, x11_gc->fg_pixel, color); + else + g_warning ("No colormap in _gdk_windowing_gc_get_foreground"); +} diff --git a/gdk/x11/gdkpango-x11.c b/gdk/x11/gdkpango-x11.c index eb9923fc3..de7577bee 100644 --- a/gdk/x11/gdkpango-x11.c +++ b/gdk/x11/gdkpango-x11.c @@ -25,6 +25,116 @@ #include "gdkdisplay-x11.h" #include "gdkpango.h" #include +#include + +#include + +typedef struct _GdkX11Renderer GdkX11Renderer; +typedef struct _GdkX11RendererClass GdkX11RendererClass; + +#define GDK_TYPE_X11_RENDERER (gdk_x11_renderer_get_type()) +#define GDK_X11_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_X11_RENDERER, GdkX11Renderer)) +#define GDK_IS_X11_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_X11_RENDERER)) +#define GDK_X11_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_X11_RENDERER, GdkX11RendererClass)) +#define GDK_IS_X11_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_X11_RENDERER)) +#define GDK_X11_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_X11_RENDERER, GdkX11RendererClass)) + +#define MAX_RENDER_PART PANGO_RENDER_PART_STRIKETHROUGH + +struct _GdkX11Renderer +{ + PangoXftRenderer parent_instance; + + XRenderPictFormat *mask_format; + + GdkDrawable *drawable; + GdkGC *gc; +}; + +struct _GdkX11RendererClass +{ + PangoXftRendererClass parent_class; +}; + +G_DEFINE_TYPE (GdkX11Renderer, gdk_x11_renderer, PANGO_TYPE_XFT_RENDERER) + +static void +gdk_x11_renderer_finalize (GObject *object) +{ + G_OBJECT_CLASS (gdk_x11_renderer_parent_class)->finalize (object); +} + +static void +gdk_x11_renderer_composite_trapezoids (PangoXftRenderer *xftrenderer, + PangoRenderPart part, + XTrapezoid *trapezoids, + int n_trapezoids) +{ + /* Because we only use this renderer for "draw_glyphs()" calls, we + * won't hit this code path much. However, it is hit for drawing + * the "unknown glyph" hex squares. We can safely ignore the part, + */ + GdkX11Renderer *x11_renderer = GDK_X11_RENDERER (xftrenderer); + + _gdk_x11_drawable_draw_xtrapezoids (x11_renderer->drawable, + x11_renderer->gc, + trapezoids, n_trapezoids); + +} + +static void +gdk_x11_renderer_composite_glyphs (PangoXftRenderer *xftrenderer, + XftFont *xft_font, + XftGlyphSpec *glyphs, + gint n_glyphs) +{ + GdkX11Renderer *x11_renderer = GDK_X11_RENDERER (xftrenderer); + + _gdk_x11_drawable_draw_xft_glyphs (x11_renderer->drawable, + x11_renderer->gc, + xft_font, glyphs, n_glyphs); +} + +static void +gdk_x11_renderer_init (GdkX11Renderer *renderer) +{ +} + +static void +gdk_x11_renderer_class_init (GdkX11RendererClass *klass) +{ + PangoXftRendererClass *xftrenderer_class = PANGO_XFT_RENDERER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + xftrenderer_class->composite_glyphs = gdk_x11_renderer_composite_glyphs; + xftrenderer_class->composite_trapezoids = gdk_x11_renderer_composite_trapezoids; + + object_class->finalize = gdk_x11_renderer_finalize; +} + +PangoRenderer * +_gdk_x11_renderer_get (GdkDrawable *drawable, + GdkGC *gc) +{ + GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen; + GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen); + GdkX11Renderer *x11_renderer; + + if (!screen_x11->renderer) + { + screen_x11->renderer = g_object_new (GDK_TYPE_X11_RENDERER, + "display", GDK_SCREEN_XDISPLAY (screen), + "screen", GDK_SCREEN_XNUMBER (screen), + NULL); + } + + x11_renderer = GDK_X11_RENDERER (screen_x11->renderer); + + x11_renderer->drawable = drawable; + x11_renderer->gc = gc; + + return screen_x11->renderer; +} /** * gdk_pango_context_get_for_screen: @@ -59,3 +169,4 @@ gdk_pango_context_get_for_screen (GdkScreen *screen) return context; } + diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index 5ded4338a..d48940fb5 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -66,9 +66,14 @@ struct _GdkGCX11 guint have_clip_mask : 1; guint depth : 8; + GdkFill fill; + GdkBitmap *stipple; + GdkPixmap *tile; + Picture fg_picture; XRenderColor fg_picture_color; gulong fg_pixel; + gulong bg_pixel; }; struct _GdkGCX11Class @@ -176,6 +181,9 @@ void _gdk_dnd_init (GdkDisplay *display); void _gdk_windowing_image_init (GdkDisplay *display); void _gdk_input_init (GdkDisplay *display); +PangoRenderer *_gdk_x11_renderer_get (GdkDrawable *drawable, + GdkGC *gc); + extern GdkDrawableClass _gdk_x11_drawable_class; extern gboolean _gdk_use_xshm; extern const int _gdk_nenvent_masks; diff --git a/gdk/x11/gdkscreen-x11.c b/gdk/x11/gdkscreen-x11.c index 0856a010f..1d181eb5c 100644 --- a/gdk/x11/gdkscreen-x11.c +++ b/gdk/x11/gdkscreen-x11.c @@ -306,6 +306,9 @@ gdk_screen_x11_finalize (GObject *object) GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object); /* int i; */ g_object_unref (screen_x11->root_window); + + if (screen_x11->renderer) + g_object_unref (screen_x11->renderer); /* Visual Part (Need to implement finalize for Visuals for a clean * finalize) */ diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h index 89aa4390d..fddc28dd0 100644 --- a/gdk/x11/gdkscreen-x11.h +++ b/gdk/x11/gdkscreen-x11.h @@ -88,6 +88,9 @@ struct _GdkScreenX11 /* Xinerama */ gint num_monitors; GdkRectangle *monitors; + + /* Pango renderer object singleton */ + PangoRenderer *renderer; }; struct _GdkScreenX11Class diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 5a21e9425..7e58a14f5 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -1226,6 +1226,7 @@ gtk_justification_get_type gtk_key_snooper_install gtk_key_snooper_remove gtk_label_get +gtk_label_get_angle gtk_label_get_attributes gtk_label_get_ellipsize gtk_label_get_justify @@ -1247,6 +1248,7 @@ gtk_label_new gtk_label_new_with_mnemonic gtk_label_parse_uline gtk_label_select_region +gtk_label_set_angle gtk_label_set_attributes gtk_label_set_ellipsize gtk_label_set_justify diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index ac148ebe4..9c26a23b6 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -49,6 +49,8 @@ typedef struct { gint width_chars; guint single_line_mode : 1; + guint have_transform : 1; + gdouble angle; } GtkLabelPrivate; @@ -88,7 +90,8 @@ enum { PROP_SELECTION_BOUND, PROP_ELLIPSIZE, PROP_WIDTH_CHARS, - PROP_SINGLE_LINE_MODE + PROP_SINGLE_LINE_MODE, + PROP_ANGLE }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -468,6 +471,27 @@ gtk_label_class_init (GtkLabelClass *class) P_("Whether the label is in single line mode"), FALSE, G_PARAM_READWRITE)); + + /** + * GtkLabel:angle: + * + * The angle that the baseline of the label makes with the horizontal, + * in degrees, measured counterclockwise. An angle of 90 reads from + * from bottom to top, an angle of 270, from top to bottom. Ignored + * if the label is selectable, wrapped, or ellipsized. + * + * Since: 2.6 + **/ + g_object_class_install_property (gobject_class, + PROP_ANGLE, + g_param_spec_double ("angle", + P_("Angle"), + P_("Angle at which the label is rotated"), + 0.0, + 360.0, + 0.0, + G_PARAM_READWRITE)); + /* * Key bindings */ @@ -596,6 +620,9 @@ gtk_label_set_property (GObject *object, case PROP_SINGLE_LINE_MODE: gtk_label_set_single_line_mode (label, g_value_get_boolean (value)); break; + case PROP_ANGLE: + gtk_label_set_angle (label, g_value_get_double (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -670,6 +697,9 @@ gtk_label_get_property (GObject *object, case PROP_SINGLE_LINE_MODE: g_value_set_boolean (value, gtk_label_get_single_line_mode (label)); break; + case PROP_ANGLE: + g_value_set_double (value, gtk_label_get_angle (label)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -686,7 +716,7 @@ gtk_label_init (GtkLabel *label) priv = GTK_LABEL_GET_PRIVATE (label); priv->width_chars = -1; - + priv->angle = 0.0; label->label = NULL; label->jtype = GTK_JUSTIFY_LEFT; @@ -1646,6 +1676,28 @@ gtk_label_ensure_layout (GtkLabel *label) PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */ GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label); + if (priv->angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info) + { + /* We rotate the standard singleton PangoContext for the widget, + * depending on the fact that it's meant pretty much exclusively + * for our use. + */ + PangoMatrix matrix = PANGO_MATRIX_INIT; + + pango_matrix_rotate (&matrix, priv->angle); + + pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix); + + priv->have_transform = TRUE; + } + else + { + if (priv->have_transform) + pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL); + + priv->have_transform = FALSE; + } + label->layout = gtk_widget_create_pango_layout (widget, label->text); if (label->effective_attrs) @@ -1750,6 +1802,55 @@ gtk_label_ensure_layout (GtkLabel *label) } } +/* Gets the bounds of a layout in device coordinates. Note cut-and-paste + * between here and gdkpango.c */ +static void +get_rotated_layout_bounds (PangoLayout *layout, + GdkRectangle *rect) +{ + PangoContext *context = pango_layout_get_context (layout); + const PangoMatrix *matrix = pango_context_get_matrix (context); + gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */ + PangoRectangle logical_rect; + gint i, j; + + pango_layout_get_extents (layout, NULL, &logical_rect); + + for (i = 0; i < 2; i++) + { + gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width; + for (j = 0; j < 2; j++) + { + gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height; + + gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0; + gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0; + + if (i == 0 && j == 0) + { + x_min = x_max = xt; + y_min = y_max = yt; + } + else + { + if (xt < x_min) + x_min = xt; + if (yt < y_min) + y_min = yt; + if (xt > x_max) + x_max = xt; + if (yt > y_max) + y_max = yt; + } + } + } + + rect->x = floor (x_min); + rect->width = ceil (x_max) - rect->x; + rect->y = floor (y_min); + rect->height = floor (y_max) - rect->y; +} + static void gtk_label_size_request (GtkWidget *widget, GtkRequisition *requisition) @@ -1787,9 +1888,21 @@ gtk_label_size_request (GtkWidget *widget, width = label->misc.xpad * 2; height = label->misc.ypad * 2; - pango_layout_get_extents (label->layout, NULL, &logical_rect); aux_info = _gtk_widget_get_aux_info (widget, FALSE); + if (priv->have_transform) + { + GdkRectangle rect; + + get_rotated_layout_bounds (label->layout, &rect); + + requisition->width = width + rect.width; + requisition->height = height + rect.height; + return; + } + else + pango_layout_get_extents (label->layout, NULL, &logical_rect); + if (label->ellipsize || priv->width_chars > 0) { PangoContext *context; @@ -1903,37 +2016,6 @@ gtk_label_direction_changed (GtkWidget *widget, GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir); } -#if 0 -static void -gtk_label_paint_word (GtkLabel *label, - gint x, - gint y, - GtkLabelWord *word, - GdkRectangle *area) -{ - GtkWidget *widget = GTK_WIDGET (label); - GtkLabelULine *uline; - gchar *tmp_str; - - tmp_str = gdk_wcstombs (word->beginning); - if (tmp_str) - { - gtk_paint_string (widget->style, widget->window, widget->state, - area, widget, "label", - x + word->x, - y + word->y, - tmp_str); - g_free (tmp_str); - } - - for (uline = word->uline; uline; uline = uline->next) - gtk_paint_hline (widget->style, widget->window, - widget->state, area, - widget, "label", - x + uline->x1, x + uline->x2, y + uline->y); -} -#endif - static void get_layout_location (GtkLabel *label, gint *xp, @@ -2807,6 +2889,70 @@ gtk_label_get_selectable (GtkLabel *label) return label->select_info != NULL; } +/** + * gtk_label_set_angle: + * @label: a #GtkLabel + * @angle: the angle that the baseline of the label makes with + * the horizontal, in degrees, measured counterclockwise + * + * Sets the angle of rotation for the label. An angle of 90 reads from + * from bottom to top, an angle of 270, from top to bottom. The angle + * setting for the label is ignored if the label is selectable, + * wrapped, or ellipsized. + * + * Since: 2.6 + **/ +void +gtk_label_set_angle (GtkLabel *label, + gdouble angle) +{ + GtkLabelPrivate *priv; + + g_return_if_fail (GTK_IS_LABEL (label)); + + priv = GTK_LABEL_GET_PRIVATE (label); + + /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because + * double property ranges are inclusive, and changing 360 to 0 would + * make a property editor behave strangely. + */ + if (angle < 0 || angle > 360.0) + angle = angle - 360. * floor (angle / 360.); + + if (angle != priv->angle) + { + priv->angle = angle; + + gtk_label_clear_layout (label); + gtk_widget_queue_resize (GTK_WIDGET (label)); + + g_object_notify (G_OBJECT (label), "angle"); + } +} + +/** + * gtk_label_get_angle: + * @label: a #GtkLabel + * + * Gets the angle of rotation for the label. See + * gtk_label_set_angle. + * + * Return value: the angle of rotation for the label + * + * Since: 2.6 + **/ +gdouble +gtk_label_get_angle (GtkLabel *label) +{ + GtkLabelPrivate *priv; + + g_return_val_if_fail (GTK_IS_LABEL (label), 0.0); + + priv = GTK_LABEL_GET_PRIVATE (label); + + return priv->angle; +} + static void gtk_label_set_selection_text (GtkLabel *label, GtkSelectionData *selection_data) diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h index 5da9c47ef..c6e9407f8 100644 --- a/gtk/gtklabel.h +++ b/gtk/gtklabel.h @@ -143,6 +143,9 @@ gboolean gtk_label_get_line_wrap (GtkLabel *label); void gtk_label_set_selectable (GtkLabel *label, gboolean setting); gboolean gtk_label_get_selectable (GtkLabel *label); +void gtk_label_set_angle (GtkLabel *label, + gdouble angle); +gdouble gtk_label_get_angle (GtkLabel *label); void gtk_label_select_region (GtkLabel *label, gint start_offset, gint end_offset); diff --git a/gtk/gtktextdisplay.c b/gtk/gtktextdisplay.c index bb7867f37..4b621b431 100644 --- a/gtk/gtktextdisplay.c +++ b/gtk/gtktextdisplay.c @@ -82,452 +82,379 @@ * use the semi-public headers, as with gtktextview.c. */ -#include +#define GTK_TYPE_TEXT_RENDERER (gtk_text_renderer_get_type()) +#define GTK_TEXT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_TEXT_RENDERER, GtkTextRenderer)) +#define GTK_IS_TEXT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_TEXT_RENDERER)) +#define GTK_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass)) +#define GTK_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_RENDERER)) +#define GTK_TEXT_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass)) -typedef struct _GtkTextRenderState GtkTextRenderState; +typedef struct _GtkTextRenderer GtkTextRenderer; +typedef struct _GtkTextRendererClass GtkTextRendererClass; -struct _GtkTextRenderState +struct _GtkTextRenderer { - GtkWidget *widget; + GdkPangoRenderer parent_instance; - GtkTextAppearance *last_appearance; - GtkTextAppearance *last_bg_appearance; - GdkGC *fg_gc; - GdkGC *bg_gc; - GdkGC *error_gc; + GdkScreen *screen; + + GtkWidget *widget; + GdkDrawable *drawable; GdkRectangle clip_rect; + + GdkColor *error_color; /* Error underline color for this widget */ + GList *widgets; /* widgets encountered when drawing */ + + gboolean selected; }; -static void get_item_properties (PangoItem *item, - GtkTextAppearance **appearance); -static GdkRegion *get_selected_clip (GtkTextRenderState *render_state, - PangoLayout *layout, - PangoLayoutLine *line, - int x, - int y, - int height, - int start_index, - int end_index); - -static GtkTextRenderState * -gtk_text_render_state_new (GtkWidget *widget, - GdkDrawable *drawable, - GdkRectangle *clip_rect) +struct _GtkTextRendererClass { - GtkTextRenderState *state = g_new0 (GtkTextRenderState, 1); + GdkPangoRendererClass parent_class; +}; - state->widget = widget; - state->fg_gc = gdk_gc_new (drawable); - state->bg_gc = gdk_gc_new (drawable); +G_DEFINE_TYPE (GtkTextRenderer, gtk_text_renderer, GDK_TYPE_PANGO_RENDERER) - state->clip_rect = *clip_rect; +static GdkColor * +text_renderer_get_error_color (GtkTextRenderer *text_renderer) +{ + static const GdkColor red = { 0, 0xffff, 0, 0 }; - return state; + if (!text_renderer->error_color) + gtk_widget_style_get (text_renderer->widget, + "error-underline_color", &text_renderer->error_color, + NULL); + + if (!text_renderer->error_color) + text_renderer->error_color = gdk_color_copy (&red); + + return text_renderer->error_color; } static void -gtk_text_render_state_destroy (GtkTextRenderState *state) +text_renderer_set_gdk_color (GtkTextRenderer *text_renderer, + PangoRenderPart part, + GdkColor *gdk_color) { - g_object_unref (state->fg_gc); - g_object_unref (state->bg_gc); - if (state->error_gc) - g_object_unref (state->error_gc); + PangoRenderer *renderer = PANGO_RENDERER (text_renderer); + + if (gdk_color) + { + PangoColor color; - g_free (state); + color.red = gdk_color->red; + color.green = gdk_color->green; + color.blue = gdk_color->blue; + + pango_renderer_set_color (renderer, part, &color); + } + else + pango_renderer_set_color (renderer, part, NULL); + } -static void -gtk_text_render_state_set_color (GtkTextRenderState *state, - GdkGC *gc, - GdkColor *color) +static GtkTextAppearance * +get_item_appearance (PangoItem *item) { - gdk_colormap_alloc_color (gtk_widget_get_colormap (state->widget), color, FALSE, TRUE); - gdk_gc_set_foreground (gc, color); + GSList *tmp_list = item->analysis.extra_attrs; + + while (tmp_list) + { + PangoAttribute *attr = tmp_list->data; + + if (attr->klass->type == gtk_text_attr_appearance_type) + return &((GtkTextAttrAppearance *)attr)->appearance; + + tmp_list = tmp_list->next; + } + + return NULL; } static void -gtk_text_render_state_update (GtkTextRenderState *state, - GtkTextAppearance *new_appearance) +gtk_text_renderer_prepare_run (PangoRenderer *renderer, + PangoLayoutRun *run) { - GdkScreen *screen = gtk_widget_get_screen (state->widget); + GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer); + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + GdkColor *bg_color, *fg_color, *underline_color; + GdkPixmap *fg_stipple, *bg_stipple; + GtkTextAppearance *appearance; + + PANGO_RENDERER_CLASS (gtk_text_renderer_parent_class)->prepare_run (renderer, run); + + appearance = get_item_appearance (run->item); + g_assert (appearance != NULL); - if (!state->last_appearance || - !gdk_color_equal (&new_appearance->fg_color, &state->last_appearance->fg_color)) - gtk_text_render_state_set_color (state, state->fg_gc, &new_appearance->fg_color); + if (appearance->draw_bg && !text_renderer->selected) + bg_color = &appearance->bg_color; + else + bg_color = NULL; + + text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color); - if (!state->last_appearance || - new_appearance->fg_stipple != state->last_appearance->fg_stipple) + if (text_renderer->selected) { - if (new_appearance->fg_stipple) - { - if (screen == gdk_drawable_get_screen (new_appearance->fg_stipple)) - { - gdk_gc_set_fill (state->fg_gc, GDK_STIPPLED); - gdk_gc_set_stipple (state->fg_gc, new_appearance->fg_stipple); - } - else - g_warning ("gtk_text_render_state_update:\n" - "The foreground stipple bitmap has been created on the wrong screen.\n" - "Ignoring the stipple bitmap information."); - } + if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) + fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED]; else - { - gdk_gc_set_fill (state->fg_gc, GDK_SOLID); - } + fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE]; } + else + fg_color = &appearance->fg_color; - if (new_appearance->draw_bg) - { - if (!state->last_bg_appearance || - !gdk_color_equal (&new_appearance->bg_color, &state->last_bg_appearance->bg_color)) - gtk_text_render_state_set_color (state, state->bg_gc, &new_appearance->bg_color); + text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_color); + text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color); - if (!state->last_bg_appearance || - new_appearance->bg_stipple != state->last_bg_appearance->bg_stipple) - { - if (new_appearance->bg_stipple) - { - if (screen == gdk_drawable_get_screen (new_appearance->bg_stipple)) - { - gdk_gc_set_fill (state->bg_gc, GDK_STIPPLED); - gdk_gc_set_stipple (state->bg_gc, new_appearance->bg_stipple); - } - else - g_warning ("gtk_text_render_state_update:\n" - "The background stipple bitmap has been created on the wrong screen.\n" - "Ignoring the stipple bitmap information."); + if (appearance->underline == PANGO_UNDERLINE_ERROR) + underline_color = text_renderer_get_error_color (text_renderer); + else + underline_color = fg_color; - } - else - { - gdk_gc_set_fill (state->bg_gc, GDK_SOLID); - } - } + text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color); - state->last_bg_appearance = new_appearance; + fg_stipple = appearance->fg_stipple; + if (fg_stipple && text_renderer->screen != gdk_drawable_get_screen (fg_stipple)) + { + g_warning ("gtk_text_renderer_prepare_run:\n" + "The foreground stipple bitmap has been created on the wrong screen.\n" + "Ignoring the stipple bitmap information."); + fg_stipple = NULL; } + + gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, fg_stipple); + gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_stipple); + gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, fg_stipple); - state->last_appearance = new_appearance; + bg_stipple = appearance->draw_bg ? appearance->bg_stipple : NULL; + + if (bg_stipple && text_renderer->screen != gdk_drawable_get_screen (bg_stipple)) + { + g_warning ("gtk_text_renderer_prepare_run:\n" + "The background stipple bitmap has been created on the wrong screen.\n" + "Ignoring the stipple bitmap information."); + bg_stipple = NULL; + } + + gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, bg_stipple); } -static GdkGC * -gtk_text_render_state_get_error_gc (GtkTextRenderState *state) +static void +gtk_text_renderer_draw_shape (PangoRenderer *renderer, + PangoAttrShape *attr, + int x, + int y) { - if (!state->error_gc) + GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer); + GdkGC *fg_gc; + + if (text_renderer->selected) + { + if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) + fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED]; + else + fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED]; + } + else + fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL]; + + if (attr->data == NULL) { - static const GdkColor red = { 0, 0xffff, 0, 0 }; + /* This happens if we have an empty widget anchor. Draw + * something empty-looking. + */ + GdkRectangle shape_rect, draw_rect; - GdkGCValues gc_values; - GdkGCValuesMask gc_values_mask; - GdkColor *underline_color; - GtkWidget *widget = state->widget; - - gtk_widget_style_get (widget, "error-underline_color", &underline_color, NULL); + shape_rect.x = PANGO_PIXELS (x); + shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y); + shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x; + shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y; - gc_values_mask = GDK_GC_FOREGROUND; - if (underline_color) + if (gdk_rectangle_intersect (&shape_rect, &text_renderer->clip_rect, + &draw_rect)) { - gc_values.foreground = *underline_color; - gdk_color_free (underline_color); + gdk_draw_rectangle (text_renderer->drawable, fg_gc, + FALSE, shape_rect.x, shape_rect.y, + shape_rect.width, shape_rect.height); + + gdk_draw_line (text_renderer->drawable, fg_gc, + shape_rect.x, shape_rect.y, + shape_rect.x + shape_rect.width, + shape_rect.y + shape_rect.height); + + gdk_draw_line (text_renderer->drawable, fg_gc, + shape_rect.x + shape_rect.width, shape_rect.y, + shape_rect.x, + shape_rect.y + shape_rect.height); } - else - gc_values.foreground = red; + } + else if (GDK_IS_PIXBUF (attr->data)) + { + gint width, height; + GdkRectangle pixbuf_rect, draw_rect; + GdkPixbuf *pixbuf; - gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground); - state->error_gc = gdk_gc_new_with_values (widget->window, &gc_values, gc_values_mask); + pixbuf = GDK_PIXBUF (attr->data); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + pixbuf_rect.x = PANGO_PIXELS (x); + pixbuf_rect.y = PANGO_PIXELS (y) - height; + pixbuf_rect.width = width; + pixbuf_rect.height = height; + + if (gdk_rectangle_intersect (&pixbuf_rect, &text_renderer->clip_rect, + &draw_rect)) + { + gdk_draw_pixbuf (text_renderer->drawable, + fg_gc, + pixbuf, + draw_rect.x - pixbuf_rect.x, + draw_rect.y - pixbuf_rect.y, + draw_rect.x, draw_rect.y, + draw_rect.width, + draw_rect.height, + GDK_RGB_DITHER_NORMAL, + 0, 0); + } } + else if (GTK_IS_WIDGET (attr->data)) + { + GtkWidget *widget; + + widget = GTK_WIDGET (attr->data); - return state->error_gc; + text_renderer->widgets = g_list_prepend (text_renderer->widgets, + g_object_ref (widget)); + } + else + g_assert_not_reached (); /* not a pixbuf or widget */ } static void -get_shape_extents (PangoLayoutRun *run, - PangoRectangle *ink_rect, - PangoRectangle *logical_rect) +gtk_text_renderer_finalize (GObject *object) { - GSList *tmp_list = run->item->analysis.extra_attrs; - - while (tmp_list) - { - PangoAttribute *attr = tmp_list->data; - - if (attr->klass->type == PANGO_ATTR_SHAPE) - { - if (logical_rect) - *logical_rect = ((PangoAttrShape *)attr)->logical_rect; - - if (ink_rect) - *ink_rect = ((PangoAttrShape *)attr)->ink_rect; - - return; - } - - tmp_list = tmp_list->next; - } + G_OBJECT_CLASS (gtk_text_renderer_parent_class)->finalize (object); +} - g_assert_not_reached (); +static void +gtk_text_renderer_init (GtkTextRenderer *renderer) +{ } static void -render_layout_line (GdkDrawable *drawable, - GtkTextRenderState *render_state, - PangoLayoutLine *line, - GSList **shaped_pointer, - int x, - int y, - gboolean selected, - GList **widgets) +gtk_text_renderer_class_init (GtkTextRendererClass *klass) { - GSList *tmp_list = line->runs; - PangoRectangle overall_rect; - PangoRectangle logical_rect; - PangoRectangle ink_rect; - gint x_off = 0; - GdkGC *fg_gc; + GObjectClass *object_class = G_OBJECT_CLASS (klass); - pango_layout_line_get_extents (line, NULL, &overall_rect); + PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); + + renderer_class->prepare_run = gtk_text_renderer_prepare_run; + renderer_class->draw_shape = gtk_text_renderer_draw_shape; - while (tmp_list) - { - PangoLayoutRun *run = tmp_list->data; - GtkTextAppearance *appearance; - gint risen_y; - gint shaped_width_pixels = 0; - gboolean need_ink = FALSE; - - tmp_list = tmp_list->next; + object_class->finalize = gtk_text_renderer_finalize; +} - get_item_properties (run->item, &appearance); +static void +text_renderer_set_selected (GtkTextRenderer *text_renderer, + gboolean selected) +{ + text_renderer->selected = selected; +} - g_assert (appearance != NULL); - - risen_y = y - PANGO_PIXELS (appearance->rise); - - if (selected) - { - if (GTK_WIDGET_HAS_FOCUS (render_state->widget)) - fg_gc = render_state->widget->style->text_gc[GTK_STATE_SELECTED]; - else - fg_gc = render_state->widget->style->text_gc [GTK_STATE_ACTIVE]; - } - else - { - gtk_text_render_state_update (render_state, appearance); - - fg_gc = render_state->fg_gc; - } - - if (appearance->underline != PANGO_UNDERLINE_NONE || - appearance->strikethrough) - need_ink = TRUE; - - if (appearance->is_text) - { - if (need_ink) - pango_glyph_string_extents (run->glyphs, run->item->analysis.font, - &ink_rect, &logical_rect); - else - pango_glyph_string_extents (run->glyphs, run->item->analysis.font, - NULL, &logical_rect); - } - else - { - if (need_ink) - get_shape_extents (run, &ink_rect, &logical_rect); - else - get_shape_extents (run, NULL, &logical_rect); - } - - if (appearance->draw_bg && !selected) - gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE, - x + PANGO_PIXELS (x_off) + PANGO_PIXELS (logical_rect.x), - risen_y + PANGO_PIXELS (logical_rect.y), - PANGO_PIXELS (logical_rect.width), - PANGO_PIXELS (logical_rect.height)); - - if (appearance->is_text) - gdk_draw_glyphs (drawable, fg_gc, - run->item->analysis.font, - x + PANGO_PIXELS (x_off), - risen_y, run->glyphs); - else - { - GObject *shaped = (*shaped_pointer)->data; +static void +text_renderer_begin (GtkTextRenderer *text_renderer, + GtkWidget *widget, + GdkDrawable *drawable, + GdkRectangle *clip_rect) +{ + text_renderer->widget = widget; + text_renderer->drawable = drawable; + text_renderer->clip_rect = *clip_rect; - *shaped_pointer = (*shaped_pointer)->next; + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), drawable); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), + widget->style->text_gc[widget->state]); +} - if (shaped == NULL) - { - /* This happens if we have an empty widget anchor. Draw - * something empty-looking. - */ - GdkRectangle shape_rect, draw_rect; - - shape_rect.x = x + x_off / PANGO_SCALE; - shape_rect.y = risen_y - PANGO_PIXELS (logical_rect.height); - shape_rect.width = PANGO_PIXELS (logical_rect.width); - shape_rect.height = PANGO_PIXELS (logical_rect.height); - - if (gdk_rectangle_intersect (&shape_rect, &render_state->clip_rect, - &draw_rect)) - { - gdk_draw_rectangle (drawable, render_state->fg_gc, - FALSE, shape_rect.x, shape_rect.y, - shape_rect.width, shape_rect.height); - - gdk_draw_line (drawable, render_state->fg_gc, - shape_rect.x, shape_rect.y, - shape_rect.x + shape_rect.width, - shape_rect.y + shape_rect.height); - - gdk_draw_line (drawable, render_state->fg_gc, - shape_rect.x + shape_rect.width, shape_rect.y, - shape_rect.x, - shape_rect.y + shape_rect.height); - } +/* Returns a GSList of (referenced) widgets encountered while drawing. + */ +static GList * +text_renderer_end (GtkTextRenderer *text_renderer) +{ + GList *widgets = text_renderer->widgets; - shaped_width_pixels = shape_rect.width; - } - else if (GDK_IS_PIXBUF (shaped)) - { - gint width, height; - GdkRectangle pixbuf_rect, draw_rect; - GdkPixbuf *pixbuf; + text_renderer->widget = NULL; + text_renderer->drawable = NULL; - pixbuf = GDK_PIXBUF (shaped); - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); + text_renderer->widgets = NULL; - pixbuf_rect.x = x + x_off / PANGO_SCALE; - pixbuf_rect.y = risen_y - height; - pixbuf_rect.width = width; - pixbuf_rect.height = height; + if (text_renderer->error_color) + { + gdk_color_free (text_renderer->error_color); + text_renderer->error_color = NULL; + } - if (gdk_rectangle_intersect (&pixbuf_rect, &render_state->clip_rect, - &draw_rect)) - { - gdk_draw_pixbuf (drawable, - render_state->fg_gc, - pixbuf, - draw_rect.x - pixbuf_rect.x, - draw_rect.y - pixbuf_rect.y, - draw_rect.x, draw_rect.y, - draw_rect.width, - draw_rect.height, - GDK_RGB_DITHER_NORMAL, - 0, 0); - } + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), NULL); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL); - shaped_width_pixels = width; - } - else if (GTK_IS_WIDGET (shaped)) - { - GtkWidget *widget; - - widget = GTK_WIDGET (shaped); - - shaped_width_pixels = widget->allocation.width; + return widgets; +} - if (widgets) - { - g_object_ref (widget); - *widgets = g_list_prepend (*widgets, widget); - } - } - else - g_assert_not_reached (); /* not a pixbuf or widget */ - } - switch (appearance->underline) - { - case PANGO_UNDERLINE_NONE: - break; - case PANGO_UNDERLINE_DOUBLE: - g_assert (need_ink); - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, - risen_y + 3, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, - risen_y + 3); - /* Fall through */ - case PANGO_UNDERLINE_SINGLE: - g_assert (need_ink); - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, - risen_y + 1, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, - risen_y + 1); - break; - case PANGO_UNDERLINE_ERROR: - g_assert (need_ink); - { - GdkGC *error_gc = gtk_text_render_state_get_error_gc (render_state); - - int point_x, point_y; - int counter = 0; - int end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE; - - for (point_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1; - point_x <= end_x; - point_x += 2) - { - point_y = counter ? risen_y + 1 : risen_y + 2; - - gdk_draw_line (drawable, error_gc, - point_x, point_y, - MIN (point_x + 1, end_x), point_y); - - counter = (counter + 1) % 2; - } - } - break; - case PANGO_UNDERLINE_LOW: - g_assert (need_ink); - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, - risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, - risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1); - break; - } +static GdkRegion * +get_selected_clip (GtkTextRenderer *text_renderer, + PangoLayout *layout, + PangoLayoutLine *line, + int x, + int y, + int height, + int start_index, + int end_index) +{ + gint *ranges; + gint n_ranges, i; + GdkRegion *clip_region = gdk_region_new (); + GdkRegion *tmp_region; - if (appearance->strikethrough) - { - gint strikethrough_y = risen_y + (0.3 * logical_rect.y) / PANGO_SCALE; + pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); - g_assert (need_ink); - - gdk_draw_line (drawable, fg_gc, - x + (x_off + ink_rect.x) / PANGO_SCALE - 1, strikethrough_y, - x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, strikethrough_y); - } + for (i=0; i < n_ranges; i++) + { + GdkRectangle rect; - if (appearance->is_text) - x_off += logical_rect.width; - else - x_off += shaped_width_pixels * PANGO_SCALE; + rect.x = x + PANGO_PIXELS (ranges[2*i]); + rect.y = y; + rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]); + rect.height = height; + + gdk_region_union_with_rect (clip_region, &rect); } + + tmp_region = gdk_region_rectangle (&text_renderer->clip_rect); + gdk_region_intersect (clip_region, tmp_region); + gdk_region_destroy (tmp_region); + + g_free (ranges); + return clip_region; } static void -render_para (GdkDrawable *drawable, - GtkTextRenderState *render_state, +render_para (GtkTextRenderer *text_renderer, GtkTextLineDisplay *line_display, /* Top-left corner of paragraph including all margins */ int x, int y, int selection_start_index, - int selection_end_index, - GList **widgets) + int selection_end_index) { - GSList *shaped_pointer = line_display->shaped_objects; PangoLayout *layout = line_display->layout; int byte_offset = 0; PangoLayoutIter *iter; PangoRectangle layout_logical; int screen_width; - GdkGC *fg_gc, *bg_gc; + GdkGC *selection_gc, *fg_gc; gint state; gboolean first = TRUE; @@ -543,13 +470,13 @@ render_para (GdkDrawable *drawable, screen_width = line_display->total_width; - if (GTK_WIDGET_HAS_FOCUS (render_state->widget)) + if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget)) state = GTK_STATE_SELECTED; else state = GTK_STATE_ACTIVE; - fg_gc = render_state->widget->style->text_gc [state]; - bg_gc = render_state->widget->style->base_gc [state]; + selection_gc = text_renderer->widget->style->base_gc [state]; + fg_gc = text_renderer->widget->style->text_gc[text_renderer->widget->state]; do { @@ -589,59 +516,65 @@ render_para (GdkDrawable *drawable, if (selection_start_index < byte_offset && selection_end_index > line->length + byte_offset) /* All selected */ { - gdk_draw_rectangle (drawable, - bg_gc, + gdk_draw_rectangle (text_renderer->drawable, + selection_gc, TRUE, x + line_display->left_margin, selection_y, screen_width, selection_height); - render_layout_line (drawable, render_state, line, &shaped_pointer, - x + PANGO_PIXELS (line_rect.x), - y + PANGO_PIXELS (baseline), - TRUE, - widgets); + text_renderer_set_selected (text_renderer, TRUE); + pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), + line, + PANGO_SCALE * x + line_rect.x, + PANGO_SCALE * y + baseline); } else { - GSList *shaped_pointer_tmp = shaped_pointer; - - render_layout_line (drawable, render_state, - line, &shaped_pointer, - x + PANGO_PIXELS (line_rect.x), - y + PANGO_PIXELS (baseline), - FALSE, - widgets); + text_renderer_set_selected (text_renderer, FALSE); + pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), + line, + PANGO_SCALE * x + line_rect.x, + PANGO_SCALE * y + baseline); if (selection_start_index <= byte_offset + line->length && selection_end_index > byte_offset) /* Some selected */ { - GdkRegion *clip_region = get_selected_clip (render_state, layout, line, + GdkRegion *clip_region = get_selected_clip (text_renderer, layout, line, x + line_display->x_offset, selection_y, selection_height, selection_start_index, selection_end_index); + + /* When we change the clip on the foreground GC, we have to set + * it on the rendererer again, since the rendererer might have + * copied the GC to change attributes. + */ + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL); + gdk_gc_set_clip_region (selection_gc, clip_region); gdk_gc_set_clip_region (fg_gc, clip_region); - gdk_gc_set_clip_region (bg_gc, clip_region); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc); - gdk_draw_rectangle (drawable, - bg_gc, + gdk_draw_rectangle (text_renderer->drawable, + selection_gc, TRUE, x + PANGO_PIXELS (line_rect.x), selection_y, PANGO_PIXELS (line_rect.width), selection_height); - render_layout_line (drawable, render_state, line, &shaped_pointer_tmp, - x + PANGO_PIXELS (line_rect.x), - y + PANGO_PIXELS (baseline), - TRUE, - widgets); + text_renderer_set_selected (text_renderer, TRUE); + pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer), + line, + PANGO_SCALE * x + line_rect.x, + PANGO_SCALE * y + baseline); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL); + gdk_gc_set_clip_region (selection_gc, NULL); gdk_gc_set_clip_region (fg_gc, NULL); - gdk_gc_set_clip_region (bg_gc, NULL); - + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc); + gdk_region_destroy (clip_region); /* Paint in the ends of the line */ @@ -649,8 +582,8 @@ render_para (GdkDrawable *drawable, ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) || (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length))) { - gdk_draw_rectangle (drawable, - bg_gc, + gdk_draw_rectangle (text_renderer->drawable, + selection_gc, TRUE, x + line_display->left_margin, selection_y, @@ -669,8 +602,8 @@ render_para (GdkDrawable *drawable, line_display->left_margin + screen_width - PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width); - gdk_draw_rectangle (drawable, - bg_gc, + gdk_draw_rectangle (text_renderer->drawable, + selection_gc, TRUE, x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width), selection_y, @@ -687,62 +620,37 @@ render_para (GdkDrawable *drawable, pango_layout_iter_free (iter); } -static GdkRegion * -get_selected_clip (GtkTextRenderState *render_state, - PangoLayout *layout, - PangoLayoutLine *line, - int x, - int y, - int height, - int start_index, - int end_index) +static void +on_renderer_display_closed (GdkDisplay *display, + GtkTextRenderer *text_renderer) { - gint *ranges; - gint n_ranges, i; - GdkRegion *clip_region = gdk_region_new (); - GdkRegion *tmp_region; - - pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); - - for (i=0; i < n_ranges; i++) - { - GdkRectangle rect; - - rect.x = x + PANGO_PIXELS (ranges[2*i]); - rect.y = y; - rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]); - rect.height = height; - - gdk_region_union_with_rect (clip_region, &rect); - } - - tmp_region = gdk_region_rectangle (&render_state->clip_rect); - gdk_region_intersect (clip_region, tmp_region); - gdk_region_destroy (tmp_region); - - g_free (ranges); - return clip_region; + g_signal_handlers_disconnect_by_func (text_renderer->screen, + (gpointer)on_renderer_display_closed, + text_renderer); + g_object_set_data (G_OBJECT (text_renderer->screen), "gtk-text-renderer", NULL); } -static void -get_item_properties (PangoItem *item, - GtkTextAppearance **appearance) +static GtkTextRenderer * +get_text_renderer (GdkScreen *screen) { - GSList *tmp_list = item->analysis.extra_attrs; - - *appearance = NULL; + GtkTextRenderer *text_renderer; - while (tmp_list) + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + text_renderer = g_object_get_data (G_OBJECT (screen), "gtk-text-renderer"); + if (!text_renderer) { - PangoAttribute *attr = tmp_list->data; - - if (attr->klass->type == gtk_text_attr_appearance_type) - { - *appearance = &((GtkTextAttrAppearance *)attr)->appearance; - } + text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, "screen", screen, NULL); + text_renderer->screen = screen; + + g_object_set_data_full (G_OBJECT (screen), "gtk-text-renderer", text_renderer, + (GDestroyNotify)g_object_unref); - tmp_list = tmp_list->next; + g_signal_connect (gdk_screen_get_display (screen), "closed", + G_CALLBACK (on_renderer_display_closed), text_renderer); } + + return text_renderer; } void @@ -766,11 +674,12 @@ gtk_text_layout_draw (GtkTextLayout *layout, GdkRectangle clip; gint current_y; GSList *cursor_list; - GtkTextRenderState *render_state; + GtkTextRenderer *text_renderer; GtkTextIter selection_start, selection_end; gboolean have_selection = FALSE; GSList *line_list; GSList *tmp_list; + GList *tmp_widgets; g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout)); g_return_if_fail (layout->default_style != NULL); @@ -793,10 +702,9 @@ gtk_text_layout_draw (GtkTextLayout *layout, clip.width = width; clip.height = height; - render_state = gtk_text_render_state_new (widget, drawable, &clip); + text_renderer = get_text_renderer (gdk_drawable_get_screen (drawable)); - gdk_gc_set_clip_rectangle (render_state->fg_gc, &clip); - gdk_gc_set_clip_rectangle (render_state->bg_gc, &clip); + text_renderer_begin (text_renderer, widget, drawable, &clip); gtk_text_layout_wrap_loop_start (layout); @@ -850,11 +758,10 @@ gtk_text_layout_draw (GtkTextLayout *layout, } } - render_para (drawable, render_state, line_display, + render_para (text_renderer, line_display, - x_offset, current_y, - selection_start_index, selection_end_index, - widgets); + selection_start_index, selection_end_index); /* We paint the cursors last, because they overlap another chunk and need to appear on top. */ @@ -903,14 +810,20 @@ gtk_text_layout_draw (GtkTextLayout *layout, current_y += line_display->height; gtk_text_layout_free_line_display (layout, line_display); - render_state->last_appearance = NULL; - render_state->last_bg_appearance = NULL; tmp_list = g_slist_next (tmp_list); } gtk_text_layout_wrap_loop_end (layout); - gtk_text_render_state_destroy (render_state); + + tmp_widgets = text_renderer_end (text_renderer); + if (widgets) + *widgets = tmp_widgets; + else + { + g_list_foreach (tmp_widgets, (GFunc)g_object_unref, NULL); + g_list_free (tmp_widgets); + } g_slist_free (line_list); } diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index 982032835..7c5dff55f 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -1398,6 +1398,16 @@ add_generic_attrs (GtkTextLayout *layout, pango_attr_list_insert (attrs, attr); } + if (appearance->strikethrough) + { + attr = pango_attr_strikethrough_new (appearance->strikethrough); + + attr->start_index = start; + attr->end_index = start + byte_count; + + pango_attr_list_insert (attrs, attr); + } + if (appearance->rise != 0) { attr = pango_attr_rise_new (appearance->rise); @@ -1469,7 +1479,8 @@ add_pixbuf_attrs (GtkTextLayout *layout, logical_rect.width = width * PANGO_SCALE; logical_rect.height = height * PANGO_SCALE; - attr = pango_attr_shape_new (&logical_rect, &logical_rect); + attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect, + pixbuf->pixbuf, NULL, NULL); attr->start_index = start; attr->end_index = start + seg->byte_count; pango_attr_list_insert (attrs, attr); @@ -1491,6 +1502,7 @@ add_child_attrs (GtkTextLayout *layout, GtkTextChildAnchor *anchor; gint width, height; GSList *tmp_list; + GtkWidget *widget; width = 1; height = 1; @@ -1512,8 +1524,7 @@ add_child_attrs (GtkTextLayout *layout, width = req.width; height = req.height; - display->shaped_objects = - g_slist_append (display->shaped_objects, child); + widget = child; break; } @@ -1534,16 +1545,18 @@ add_child_attrs (GtkTextLayout *layout, width = 30; height = 20; - display->shaped_objects = - g_slist_append (display->shaped_objects, NULL); + widget = NULL; } + + display->shaped_objects = g_slist_append (display->shaped_objects, widget); logical_rect.x = 0; logical_rect.y = -height * PANGO_SCALE; logical_rect.width = width * PANGO_SCALE; logical_rect.height = height * PANGO_SCALE; - attr = pango_attr_shape_new (&logical_rect, &logical_rect); + attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect, + widget, NULL, NULL); attr->start_index = start; attr->end_index = start + seg->byte_count; pango_attr_list_insert (attrs, attr); diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h index fa6802ebb..f42a7c3c0 100644 --- a/gtk/gtktextlayout.h +++ b/gtk/gtktextlayout.h @@ -235,7 +235,7 @@ struct _GtkTextLineDisplay { PangoLayout *layout; GSList *cursors; - GSList *shaped_objects; + GSList *shaped_objects; /* Only for backwards compatibility */ GtkTextDirection direction; diff --git a/tests/testgtk.c b/tests/testgtk.c index b52af0828..3a1f6791a 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -2689,6 +2689,219 @@ void create_labels (GtkWidget *widget) gtk_widget_destroy (window); } +static void +on_angle_scale_changed (GtkRange *range, + GtkLabel *label) +{ + gtk_label_set_angle (GTK_LABEL (label), gtk_range_get_value (range)); +} + +static void +create_rotated_label (GtkWidget *widget) +{ + static GtkWidget *window = NULL; + GtkWidget *vbox; + GtkWidget *hscale; + GtkWidget *label; + GtkWidget *scale_label; + GtkWidget *scale_hbox; + + if (!window) + { + window = gtk_dialog_new_with_buttons ("Rotated Label", + GTK_WINDOW (gtk_widget_get_toplevel (widget)), 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + gtk_window_set_resizable (GTK_WINDOW (window), FALSE); + + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (widget)); + + g_signal_connect (window, "response", + G_CALLBACK (gtk_object_destroy), NULL); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + vbox = gtk_vbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), vbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 10); + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), "Hello World\nRotate me"); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + + scale_hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), scale_hbox, FALSE, FALSE, 0); + + scale_label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (scale_label), "Angle: "); + gtk_box_pack_start (GTK_BOX (scale_hbox), scale_label, FALSE, FALSE, 0); + + hscale = gtk_hscale_new_with_range (0, 360, 5); + g_signal_connect (hscale, "value-changed", + G_CALLBACK (on_angle_scale_changed), label); + + gtk_range_set_value (GTK_RANGE (hscale), 45); + gtk_widget_set_usize (hscale, 200, -1); + gtk_box_pack_start (GTK_BOX (scale_hbox), hscale, TRUE, TRUE, 0); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); +} + +#define DEFAULT_TEXT_RADIUS 200 + +static void +on_rotated_text_unrealize (GtkWidget *widget) +{ + g_object_set_data (G_OBJECT (widget), "text-gc", NULL); +} + +static gboolean +on_rotated_text_expose (GtkWidget *widget, + GdkEventExpose *event, + GdkPixbuf *tile_pixbuf) +{ + static const gchar *words[] = { "The", "grand", "old", "Duke", "of", "York", + "had", "10,000", "men" }; + PangoRenderer *renderer; + GdkGC *gc; + int n_words; + int i; + double radius; + PangoMatrix matrix = PANGO_MATRIX_INIT; + PangoLayout *layout; + PangoContext *context; + PangoFontDescription *desc; + + gc = g_object_get_data (G_OBJECT (widget), "text-gc"); + if (!gc) + { + static GdkColor black = { 0, 0, 0, 0 }; + + gc = gdk_gc_new (widget->window); + gdk_gc_set_rgb_fg_color (gc, &black); + + if (tile_pixbuf) + { + GdkPixmap *tile; + + gint width = gdk_pixbuf_get_width (tile_pixbuf); + gint height = gdk_pixbuf_get_height (tile_pixbuf); + + tile = gdk_pixmap_new (widget->window, width, height, -1); + gdk_draw_pixbuf (tile, gc, tile_pixbuf, + 0, 0, 0, 0, width, height, + GDK_RGB_DITHER_NORMAL, 0, 0); + + gdk_gc_set_tile (gc, tile); + gdk_gc_set_fill (gc, GDK_TILED); + + g_object_unref (tile); + } + + g_object_set_data_full (G_OBJECT (widget), "text-gc", gc, (GDestroyNotify)g_object_unref); + } + + renderer = gdk_pango_renderer_get_default (gtk_widget_get_screen (widget)); + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), widget->window); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), gc); + + radius = MIN (widget->allocation.width, widget->allocation.height) / 2.; + + pango_matrix_translate (&matrix, + radius + (widget->allocation.width - 2 * radius) / 2, + radius + (widget->allocation.height - 2 * radius) / 2); + pango_matrix_scale (&matrix, radius / DEFAULT_TEXT_RADIUS, radius / DEFAULT_TEXT_RADIUS); + + context = gtk_widget_get_pango_context (widget); + layout = pango_layout_new (context); + desc = pango_font_description_from_string ("Sans Bold 30"); + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + + n_words = G_N_ELEMENTS (words); + for (i = 0; i < n_words; i++) + { + PangoMatrix rotated_matrix = matrix; + int width, height; + + pango_matrix_rotate (&rotated_matrix, - (360. * i) / n_words); + + pango_context_set_matrix (context, &rotated_matrix); + pango_layout_context_changed (layout); + pango_layout_set_text (layout, words[i], -1); + + pango_layout_get_size (layout, &width, &height); + + pango_renderer_draw_layout (renderer, layout, + - width / 2, - DEFAULT_TEXT_RADIUS * PANGO_SCALE); + } + + gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), NULL); + gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), NULL); + + g_object_unref (layout); + + return FALSE; +} + +static void +create_rotated_text (GtkWidget *widget) +{ + static GtkWidget *window = NULL; + + if (!window) + { + const GdkColor white = { 0, 0xffff, 0xffff, 0xffff }; + GtkRequisition requisition; + GtkWidget *drawing_area; + GdkPixbuf *tile_pixbuf; + + window = gtk_dialog_new_with_buttons ("Rotated Text", + GTK_WINDOW (gtk_widget_get_toplevel (widget)), 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + gtk_window_set_resizable (GTK_WINDOW (window), TRUE); + + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (widget)); + + g_signal_connect (window, "response", + G_CALLBACK (gtk_object_destroy), NULL); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + drawing_area = gtk_drawing_area_new (); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), drawing_area, TRUE, TRUE, 0); + gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white); + + tile_pixbuf = gdk_pixbuf_new_from_file ("marble.xpm", NULL); + + g_signal_connect (drawing_area, "expose-event", + G_CALLBACK (on_rotated_text_expose), tile_pixbuf); + g_signal_connect (drawing_area, "unrealize", + G_CALLBACK (on_rotated_text_unrealize), NULL); + + gtk_widget_show_all (GTK_BIN (window)->child); + + gtk_widget_set_size_request (drawing_area, DEFAULT_TEXT_RADIUS * 2, DEFAULT_TEXT_RADIUS * 2); + gtk_widget_size_request (window, &requisition); + gtk_widget_set_size_request (drawing_area, -1, -1); + gtk_window_resize (GTK_WINDOW (window), requisition.width, requisition.height); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); +} + /* * Reparent demo */ @@ -12695,6 +12908,8 @@ struct { { "rc file", create_rc_file }, { "reparent", create_reparent }, { "resize grips", create_resize_grips }, + { "rotated label", create_rotated_label }, + { "rotated text", create_rotated_text }, { "rulers", create_rulers }, { "saved position", create_saved_position }, { "scrolled windows", create_scrolled_windows }, -- 2.43.2