Index: gdk/gdkpango.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkpango.c,v
retrieving revision 1.25
diff -u -p -r1.25 gdkpango.c
--- gdk/gdkpango.c	25 Sep 2002 07:23:52 -0000	1.25
+++ gdk/gdkpango.c	4 Jun 2003 20:41:57 -0000
@@ -163,6 +163,37 @@ gdk_pango_context_set_colormap (PangoCon
     }
 }
 
+static void
+draw_underline (GdkDrawable    *drawable,
+		GdkGC          *gc,
+		PangoUnderline  uline,
+		int             baseline_y,
+		int             low_y,
+		int             start_x,
+		int             end_x)
+{
+  switch (uline)
+    {
+    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_LOW:
+      gdk_draw_line (drawable, gc,
+		     start_x, low_y + 1,
+		     end_x,   low_y + 1);
+      break;
+    }
+}
+
 /**
  * gdk_draw_layout_line_with_colors:
  * @drawable:  the drawable on which to draw the line
@@ -195,6 +226,15 @@ gdk_draw_layout_line_with_colors (GdkDra
   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;
 
   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
   g_return_if_fail (GDK_IS_GC (gc));
@@ -206,7 +246,7 @@ gdk_draw_layout_line_with_colors (GdkDra
   
   while (tmp_list)
     {
-      PangoUnderline uline = PANGO_UNDERLINE_NONE;
+      PangoUnderline this_uline = PANGO_UNDERLINE_NONE;
       PangoLayoutRun *run = tmp_list->data;
       PangoColor fg_color, bg_color;
       gboolean strike, fg_set, bg_set, shape_set;
@@ -215,7 +255,7 @@ gdk_draw_layout_line_with_colors (GdkDra
       
       tmp_list = tmp_list->next;
       
-      gdk_pango_get_item_properties (run->item, &uline,
+      gdk_pango_get_item_properties (run->item, &this_uline,
 				     &strike,
                                      &rise,
                                      &fg_color, &fg_set,
@@ -231,7 +271,7 @@ gdk_draw_layout_line_with_colors (GdkDra
       
       if (!shape_set)
 	{
-	  if (uline == PANGO_UNDERLINE_NONE)
+	  if (this_uline == PANGO_UNDERLINE_NONE)
 	    pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
 					NULL, &logical_rect);
 	  else
@@ -281,7 +321,7 @@ gdk_draw_layout_line_with_colors (GdkDra
         }
       else
 	fg_gc = gc;
-      
+
       if (!shape_set)
         {
           gint gx, gy;
@@ -304,36 +344,58 @@ gdk_draw_layout_line_with_colors (GdkDra
                            gx, gy,
                            run->glyphs);
         }
-      
-      switch (uline)
+
+      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)))
 	{
-	case PANGO_UNDERLINE_NONE:
-	  break;
-	case PANGO_UNDERLINE_DOUBLE:
-	  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:
-	  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:
-	  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;
+	  /* 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);
+	  
+	  /* Starting a new underline run
+	   */
+	  if (last_uline != PANGO_UNDERLINE_NONE)
+	    {
+	      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);
+	    }
+
+	  if (this_uline != PANGO_UNDERLINE_NONE)
+	    {
+	      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;
+	    }
+	}
+
+      /* 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,
@@ -341,13 +403,29 @@ gdk_draw_layout_line_with_colors (GdkDra
 			 risen_y + centerline / PANGO_SCALE,
 			 x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1,
 			 risen_y + centerline / PANGO_SCALE);
-      }
-      
-      if (fg_gc != gc)
-	gdk_pango_free_gc (context, fg_gc);
+	}
+
+      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);
 }
 
 /**
