]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextdisplay.c
Correctly select default printer when there is more than one (CUPS)
[~andy/gtk] / gtk / gtktextdisplay.c
index 51ef0ae12580fc40482a82b7565d2d7a81b8ecea..a5a6cd8c5c1e40efed9225718a509f82a5e154d2 100644 (file)
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
  */
 
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include "config.h"
 #include "gtktextdisplay.h"
+#include "gtkintl.h"
+#include "gtkalias.h"
 /* DO NOT go putting private headers in here. This file should only
  * use the semi-public headers, as with gtktextview.c.
  */
 
-#include <pango/pango.h>
+#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
+enum {
+  NORMAL,
+  SELECTED,
+  CURSOR
+};
+
+struct _GtkTextRenderer
 {
-  GtkWidget *widget;
+  GdkPangoRenderer parent_instance;
 
-  GtkTextAppearance *last_appearance;
-  GtkTextAppearance *last_bg_appearance;
-  GdkGC *fg_gc;
-  GdkGC *bg_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 */
+  
+  int state;
 };
 
-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);
-
-  state->widget = widget;
-  state->fg_gc = gdk_gc_new (drawable);
-  state->bg_gc = gdk_gc_new (drawable);
-  state->clip_rect = *clip_rect;
+  GdkPangoRendererClass parent_class;
+};
 
-  return state;
-}
+G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, GDK_TYPE_PANGO_RENDERER)
 
-static void
-gtk_text_render_state_destroy (GtkTextRenderState *state)
+static GdkColor *
+text_renderer_get_error_color (GtkTextRenderer *text_renderer)
 {
-  gdk_gc_unref (state->fg_gc);
-  gdk_gc_unref (state->bg_gc);
+  static const GdkColor red = { 0, 0xffff, 0, 0 };
 
-  g_free (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);
 
-static void
-gtk_text_render_state_set_color (GtkTextRenderState *state,
-                                 GdkGC              *gc,
-                                 GdkColor           *color)
-{
-  gdk_colormap_alloc_color (gtk_widget_get_colormap (state->widget), color, FALSE, TRUE);
-  gdk_gc_set_foreground (gc, color);
+  return text_renderer->error_color;
 }
 
 static void
-gtk_text_render_state_update (GtkTextRenderState *state,
-                              GtkTextAppearance  *new_appearance)
+text_renderer_set_gdk_color (GtkTextRenderer *text_renderer,
+                            PangoRenderPart  part,
+                            GdkColor        *gdk_color)
 {
-  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);
+  PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
 
-  if (!state->last_appearance ||
-      new_appearance->fg_stipple != state->last_appearance->fg_stipple)
+  if (gdk_color)
     {
-      if (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
-        {
-          gdk_gc_set_fill (state->fg_gc, GDK_SOLID);
-        }
+      PangoColor color;
+
+      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);
+          
+}
 
-  if (new_appearance->draw_bg)
+static GtkTextAppearance *
+get_item_appearance (PangoItem *item)
+{
+  GSList *tmp_list = item->analysis.extra_attrs;
+
+  while (tmp_list)
     {
-      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);
+      PangoAttribute *attr = tmp_list->data;
 
-      if (!state->last_bg_appearance ||
-          new_appearance->bg_stipple != state->last_bg_appearance->bg_stipple)
-        {
-          if (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
-            {
-              gdk_gc_set_fill (state->bg_gc, GDK_SOLID);
-            }
-        }
+      if (attr->klass->type == gtk_text_attr_appearance_type)
+       return &((GtkTextAttrAppearance *)attr)->appearance;
 
-      state->last_bg_appearance = new_appearance;
+      tmp_list = tmp_list->next;
     }
 
-  state->last_appearance = new_appearance;
+  return NULL;
 }
 
 static void
-get_shape_extents (PangoLayoutRun *run,
-                   PangoRectangle *ink_rect,
-                   PangoRectangle *logical_rect)
+gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
+                              PangoLayoutRun *run)
 {
-  GSList *tmp_list = run->item->analysis.extra_attrs;
-    
-  while (tmp_list)
+  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 (appearance->draw_bg && text_renderer->state == NORMAL)
+    bg_color = &appearance->bg_color;
+  else
+    bg_color = NULL;
+  
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
+
+  if (text_renderer->state == SELECTED)
     {
-      PangoAttribute *attr = tmp_list->data;
+      if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+       fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED];
+      else
+       fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE];
+    }
+  else if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+    fg_color = &text_renderer->widget->style->base[GTK_STATE_NORMAL];
+  else
+    fg_color = &appearance->fg_color;
 
-      if (attr->klass->type == PANGO_ATTR_SHAPE)
-       {          
-         if (logical_rect)
-           *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
+  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 (ink_rect)
-           *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
+  if (appearance->underline == PANGO_UNDERLINE_ERROR)
+    underline_color = text_renderer_get_error_color (text_renderer);
+  else
+    underline_color = fg_color;
 
-          return;
-        }
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
 
-      tmp_list = tmp_list->next;
+  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);
 
-  g_assert_not_reached ();
+  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 void
-render_layout_line (GdkDrawable        *drawable,
-                    GtkTextRenderState *render_state,
-                    PangoLayoutLine    *line,
-                    GSList            **shaped_pointer,
-                    int                 x,
-                    int                 y,
-                    gboolean            selected)
+gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
+                             PangoAttrShape  *attr,
+                             int              x,
+                             int              y)
 {
-  GSList *tmp_list = line->runs;
-  PangoRectangle overall_rect;
-  PangoRectangle logical_rect;
-  PangoRectangle ink_rect;
-  gint x_off = 0;
+  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
   GdkGC *fg_gc;
-  
-  pango_layout_line_get_extents (line, NULL, &overall_rect);
 
-  while (tmp_list)
+  if (text_renderer->state == 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 if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+    fg_gc = text_renderer->widget->style->base_gc[GTK_STATE_NORMAL];
+  else
+    fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL];
+  
+  if (attr->data == NULL)
     {
-      PangoLayoutRun *run = tmp_list->data;
-      GtkTextAppearance *appearance;
-      gint risen_y;
-      gint shaped_width_pixels = 0;
-      gboolean need_ink = FALSE;
+      /* This happens if we have an empty widget anchor. Draw
+       * something empty-looking.
+       */
+      GdkRectangle shape_rect, draw_rect;
       
-      tmp_list = tmp_list->next;
-
-      get_item_properties (run->item, &appearance);
-
-      g_assert (appearance != 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;
       
-      risen_y = y - PANGO_PIXELS (appearance->rise);
+      if (gdk_rectangle_intersect (&shape_rect, &text_renderer->clip_rect,
+                                  &draw_rect))
+       {
+         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 if (GDK_IS_PIXBUF (attr->data))
+    {
+      gint width, height;
+      GdkRectangle pixbuf_rect, draw_rect;
+      GdkPixbuf *pixbuf;
       
-      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;
-        }
+      pixbuf = GDK_PIXBUF (attr->data);
       
-      if (appearance->underline != PANGO_UNDERLINE_NONE ||
-          appearance->strikethrough)
-        need_ink = TRUE;
+      width = gdk_pixbuf_get_width (pixbuf);
+      height = gdk_pixbuf_get_height (pixbuf);
       
-      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);
-        }
+      pixbuf_rect.x = PANGO_PIXELS (x);
+      pixbuf_rect.y = PANGO_PIXELS (y) - height;
+      pixbuf_rect.width = width;
+      pixbuf_rect.height = height;
       
-      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;
+      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);
 
-          *shaped_pointer = (*shaped_pointer)->next;
+      text_renderer->widgets = g_list_prepend (text_renderer->widgets,
+                                              g_object_ref (widget));
+    }
+  else
+    g_assert_not_reached (); /* not a pixbuf or widget */
+}
 
-          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);
-                }
-            }
-          else if (GDK_IS_PIXBUF (shaped))
-            {
-              gint width, height;
-              GdkRectangle pixbuf_rect, draw_rect;
-              GdkPixbuf *pixbuf;
+static void
+gtk_text_renderer_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
+}
 
-              pixbuf = GDK_PIXBUF (shaped);
-              
-              width = gdk_pixbuf_get_width (pixbuf);
-              height = gdk_pixbuf_get_height (pixbuf);
+static void
+_gtk_text_renderer_init (GtkTextRenderer *renderer)
+{
+}
 
-              pixbuf_rect.x = x + x_off / PANGO_SCALE;
-              pixbuf_rect.y = risen_y - height;
-              pixbuf_rect.width = width;
-              pixbuf_rect.height = height;
+static void
+_gtk_text_renderer_class_init (GtkTextRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  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;
 
-              if (gdk_rectangle_intersect (&pixbuf_rect, &render_state->clip_rect,
-                                           &draw_rect))
-                {
-                  GdkBitmap *mask = NULL;
-              
-                  if (gdk_pixbuf_get_has_alpha (pixbuf))
-                    {
-                      mask = gdk_pixmap_new (drawable,
-                                             gdk_pixbuf_get_width (pixbuf),
-                                             gdk_pixbuf_get_height (pixbuf),
-                                             1);
-
-                      gdk_pixbuf_render_threshold_alpha (pixbuf, mask,
-                                                         0, 0, 0, 0,
-                                                         gdk_pixbuf_get_width (pixbuf),
-                                                         gdk_pixbuf_get_height (pixbuf),
-                                                         128);
-
-                    }
-
-                  if (mask)
-                    {
-                      gdk_gc_set_clip_mask (render_state->fg_gc, mask);
-                      gdk_gc_set_clip_origin (render_state->fg_gc,
-                                              pixbuf_rect.x, pixbuf_rect.y);
-                    }
-
-                  gdk_pixbuf_render_to_drawable (pixbuf,
-                                                 drawable,
-                                                 render_state->fg_gc,
-                                                 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);
-
-                  if (mask)
-                    {
-                      gdk_gc_set_clip_rectangle (render_state->fg_gc,
-                                                 &render_state->clip_rect);
-                      g_object_unref (G_OBJECT (mask));
-                    }
-                }
+  object_class->finalize = gtk_text_renderer_finalize;
+}
 
-              shaped_width_pixels = width;
-            }
-          else if (GTK_IS_WIDGET (shaped))
-            {
-              gint width, height;
-              GdkRectangle draw_rect;
-              GtkWidget *widget;
-              
-              widget = GTK_WIDGET (shaped);
-              
-              width = widget->allocation.width;
-              height = widget->allocation.height;
-
-              g_print ("widget allocation at %d,%d %d x %d\n",
-                      widget->allocation.x,
-                      widget->allocation.y,
-                      widget->allocation.width,
-                      widget->allocation.height);
-              
-              if (GTK_WIDGET_DRAWABLE (widget) &&
-                  gdk_rectangle_intersect (&widget->allocation,
-                                           &render_state->clip_rect,
-                                           &draw_rect))
+static void
+text_renderer_set_state (GtkTextRenderer *text_renderer,
+                        int              state)
+{
+  text_renderer->state = state;
+}
 
-                {
-                  g_print ("drawing widget area %d,%d %d x %d\n",
-                          draw_rect.x,
-                          draw_rect.y,
-                          draw_rect.width,
-                          draw_rect.height);
+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;
 
-                  gtk_widget_draw (widget, &draw_rect);
-                }
+  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]);
+}
 
-              shaped_width_pixels = width;
-            }
-          else
-            g_assert_not_reached (); /* not a pixbuf or widget */
-        }
+/* Returns a GSList of (referenced) widgets encountered while drawing.
+ */
+static GList *
+text_renderer_end (GtkTextRenderer *text_renderer)
+{
+  GList *widgets = text_renderer->widgets;
 
-      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_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;
-        }
+  text_renderer->widget = NULL;
+  text_renderer->drawable = NULL;
 
-      if (appearance->strikethrough)
-        {          
-          gint strikethrough_y = risen_y + (0.3 * logical_rect.y) / PANGO_SCALE;
+  text_renderer->widgets = NULL;
 
-          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);
-        }
+  if (text_renderer->error_color)
+    {
+      gdk_color_free (text_renderer->error_color);
+      text_renderer->error_color = NULL;
+    }
 
-      if (appearance->is_text)
-        x_off += logical_rect.width;
-      else
-        x_off += shaped_width_pixels * PANGO_SCALE;
+  gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), NULL);
+  gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
+
+  return widgets;
+}
+
+
+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;
+
+  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 (&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,
@@ -491,13 +460,12 @@ render_para (GdkDrawable        *drawable,
              int                 selection_start_index,
              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;
@@ -513,21 +481,22 @@ 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
     {
-      PangoLayoutLine *line = pango_layout_iter_get_line (iter);
+      PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
       int selection_y, selection_height;
       int first_y, last_y;
       PangoRectangle line_rect;
       int baseline;
+      gboolean at_last_line;
       
       pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
       baseline = pango_layout_iter_get_baseline (iter);
@@ -550,8 +519,9 @@ render_para (GdkDrawable        *drawable,
           selection_y -= line_display->top_margin;
           selection_height += line_display->top_margin;
         }
-      
-      if (pango_layout_iter_at_last_line (iter))
+
+      at_last_line = pango_layout_iter_at_last_line (iter);
+      if (at_last_line)
         selection_height += line_display->bottom_margin;
       
       first = FALSE;
@@ -559,56 +529,89 @@ 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);
+         text_renderer_set_state (text_renderer, SELECTED);
+         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);
-
-          if (selection_start_index <= byte_offset + line->length &&
-              selection_end_index > byte_offset) /* Some selected */
+          if (line_display->pg_bg_color)
             {
-              GdkRegion *clip_region = get_selected_clip (render_state, layout, line,
+              GdkGC *bg_gc;
+              
+              bg_gc = gdk_gc_new (text_renderer->drawable);
+              gdk_gc_set_fill (bg_gc, GDK_SOLID);
+              gdk_gc_set_rgb_fg_color (bg_gc, line_display->pg_bg_color);
+            
+              gdk_draw_rectangle (text_renderer->drawable,
+                                  bg_gc,
+                                  TRUE,
+                                  x + line_display->left_margin,
+                                  selection_y,
+                                  screen_width,
+                                  selection_height);
+              
+              g_object_unref (bg_gc);
+            }
+        
+         text_renderer_set_state (text_renderer, NORMAL);
+         pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
+                                          line, 
+                                          PANGO_SCALE * x + line_rect.x,
+                                          PANGO_SCALE * y + baseline);
+
+         /* Check if some part of the line is selected; the newline
+          * that is after line->length for the last line of the
+          * paragraph counts as part of the line for this
+          */
+          if ((selection_start_index < byte_offset + line->length ||
+              (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
+             selection_end_index > byte_offset)
+            {
+              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);
+             text_renderer_set_state (text_renderer, SELECTED);
+             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 */
@@ -616,8 +619,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,
@@ -636,8 +639,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,
@@ -645,71 +648,98 @@ render_para (GdkDrawable        *drawable,
                                       selection_height);
                 }
             }
-        }
+         else if (line_display->has_block_cursor &&
+                  GTK_WIDGET_HAS_FOCUS (text_renderer->widget) &&
+                  byte_offset <= line_display->insert_index &&
+                  (line_display->insert_index < byte_offset + line->length ||
+                   (at_last_line && line_display->insert_index == byte_offset + line->length)))
+           {
+             GdkRectangle cursor_rect;
+             GdkGC *cursor_gc;
 
-      byte_offset += line->length;
-    }
-  while (pango_layout_iter_next_line (iter));
+             /* we draw text using base color on filled cursor rectangle of cursor color
+              * (normally white on black) */
+             cursor_gc = _gtk_widget_get_cursor_gc (text_renderer->widget);
 
-  pango_layout_iter_free (iter);
-}
+             cursor_rect.x = x + line_display->x_offset + line_display->block_cursor.x;
+             cursor_rect.y = y + line_display->block_cursor.y + line_display->top_margin;
+             cursor_rect.width = line_display->block_cursor.width;
+             cursor_rect.height = line_display->block_cursor.height;
 
-static GdkRegion *
-get_selected_clip (GtkTextRenderState *render_state,
-                   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;
+             gdk_gc_set_clip_rectangle (cursor_gc, &cursor_rect);
 
-  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+              gdk_draw_rectangle (text_renderer->drawable,
+                                  cursor_gc,
+                                  TRUE,
+                                  cursor_rect.x,
+                                  cursor_rect.y,
+                                  cursor_rect.width,
+                                  cursor_rect.height);
 
-  for (i=0; i < n_ranges; i++)
-    {
-      GdkRectangle rect;
+              gdk_gc_set_clip_region (cursor_gc, NULL);
 
-      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);
-    }
+             /* draw text under the cursor if any */
+             if (!line_display->cursor_at_line_end)
+               {
+                 GdkGC *cursor_text_gc;
 
-  tmp_region = gdk_region_rectangle (&render_state->clip_rect);
-  gdk_region_intersect (clip_region, tmp_region);
-  gdk_region_destroy (tmp_region);
+                 cursor_text_gc = text_renderer->widget->style->base_gc[text_renderer->widget->state];
+                 gdk_gc_set_clip_rectangle (cursor_text_gc, &cursor_rect);
 
-  g_free (ranges);
-  return clip_region;
+                 gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), cursor_text_gc);
+                 text_renderer_set_state (text_renderer, CURSOR);
+
+                 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), fg_gc);
+                 gdk_gc_set_clip_region (cursor_text_gc, NULL);
+               }
+           }
+        }
+
+      byte_offset += line->length;
+    }
+  while (pango_layout_iter_next_line (iter));
+
+  pango_layout_iter_free (iter);
 }
 
 static void
-get_item_properties (PangoItem          *item,
-                     GtkTextAppearance **appearance)
+on_renderer_display_closed (GdkDisplay       *display,
+                            gboolean          is_error,
+                           GtkTextRenderer  *text_renderer)
 {
-  GSList *tmp_list = item->analysis.extra_attrs;
+  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), I_("gtk-text-renderer"), NULL);
+}
 
-  *appearance = NULL;
+static GtkTextRenderer *
+get_text_renderer (GdkScreen *screen)
+{
+  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), I_("gtk-text-renderer"), text_renderer,
+                             (GDestroyNotify)g_object_unref);
 
-      tmp_list = tmp_list->next;
+      g_signal_connect_object (gdk_screen_get_display (screen), "closed",
+                               G_CALLBACK (on_renderer_display_closed),
+                               text_renderer, 0);
     }
+
+  return text_renderer;
 }
 
 void
@@ -726,16 +756,19 @@ gtk_text_layout_draw (GtkTextLayout *layout,
                       gint x,
                       gint y,
                       gint width,
-                      gint height)
+                      gint height,
+                      /* widgets to expose */
+                      GList **widgets)
 {
   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);
@@ -758,10 +791,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);
 
@@ -795,34 +827,31 @@ gtk_text_layout_draw (GtkTextLayout *layout,
               gtk_text_layout_get_iter_at_line (layout,
                                                 &line_start,
                                                 line, 0);
-              byte_count = gtk_text_iter_get_bytes_in_line (&line_start);
-          
-              /* FIXME the -1 assumes a newline I think */
-              gtk_text_layout_get_iter_at_line (layout,
-                                                &line_end,
-                                                line, byte_count - 1);
+              line_end = line_start;
+             if (!gtk_text_iter_ends_line (&line_end))
+               gtk_text_iter_forward_to_line_end (&line_end);
+              byte_count = gtk_text_iter_get_visible_line_index (&line_end);     
 
               if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
                   gtk_text_iter_compare (&selection_end, &line_start) >= 0)
                 {
                   if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
-                    selection_start_index = gtk_text_iter_get_line_index (&selection_start);
+                    selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
                   else
                     selection_start_index = -1;
 
                   if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
-                    selection_end_index = gtk_text_iter_get_line_index (&selection_end);
+                    selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
                   else
-                    selection_end_index = byte_count;
+                    selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
                 }
             }
 
-          render_para (drawable, render_state, line_display,
+          render_para (text_renderer, line_display,
                        - x_offset,
                        current_y,
                        selection_start_index, selection_end_index);
 
-
           /* We paint the cursors last, because they overlap another chunk
          and need to appear on top. */
 
@@ -848,32 +877,21 @@ gtk_text_layout_draw (GtkTextLayout *layout,
              GtkTextDirection dir;
              GdkRectangle cursor_location;
 
-              GdkGC *gc;
-
-              if (cursor->is_strong)
-                gc = cursor_gc;
-              else
-                gc = widget->style->text_gc[GTK_STATE_NORMAL];
-
+              dir = line_display->direction;
              if (have_strong && have_weak)
                {
-                 dir = line_display->direction;
                  if (!cursor->is_strong)
                    dir = (dir == GTK_TEXT_DIR_RTL) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
                }
-             else
-               {
-                 dir = GTK_TEXT_DIR_NONE;
-               }
  
              cursor_location.x = line_display->x_offset + cursor->x - x_offset;
              cursor_location.y = current_y + line_display->top_margin + cursor->y;
              cursor_location.width = 0;
              cursor_location.height = cursor->height;
-             gdk_gc_set_clip_rectangle(gc, &clip);
-             _gtk_draw_insertion_cursor (drawable, gc, &cursor_location, dir);
-              gdk_gc_set_clip_rectangle (gc, NULL);
+
+             gtk_draw_insertion_cursor (widget, drawable, &clip, &cursor_location,
+                                        cursor->is_strong,
+                                        dir, have_strong && have_weak);
 
               cursor_list = cursor_list->next;
             }
@@ -881,14 +899,23 @@ 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);
 }
+
+#define __GTK_TEXT_DISPLAY_C__
+#include "gtkaliasdef.c"