Patch to add word underlining



Here's my first attempt at hacking Pango. It does word-underlining. 

It creates separate PangoItems for word/non-word runs when
word-underlining is on. And for non-word runs it replaces the
PANGO_UNDERLINE_WORD with PANGO_UNDERLINE_NONE so they don't get
underlined.

I also had to hack gtktextdisplay.c a little, as it handles underlining
itself.

I'm just posting to ask if this is the correct way to do word
underlining. Note that the patch isn't quite complete. It may need a few
more tiny changes to GDK and GTK+. Also, I'm not sure if we want
PANGO_UNDERLINE_WORD_SINGLE/DOUBLE/LOW. They would be simple to add, so
I don't see why not.

Damon

--- pango-attributes.h.orig	Tue Sep 18 21:05:17 2001
+++ pango-attributes.h	Mon Oct 21 02:42:42 2002
@@ -87,7 +87,8 @@
   PANGO_UNDERLINE_NONE,
   PANGO_UNDERLINE_SINGLE,
   PANGO_UNDERLINE_DOUBLE,
-  PANGO_UNDERLINE_LOW
+  PANGO_UNDERLINE_LOW,
+  PANGO_UNDERLINE_WORD
 } PangoUnderline;
 
 struct _PangoAttribute
--- pango-context.c.orig	Mon Oct 14 23:50:47 2002
+++ pango-context.c	Mon Oct 21 18:44:20 2002
@@ -385,6 +385,7 @@
   const char *p;
   const char *next;
   GList *result = NULL;
+  gboolean word_underline_on = FALSE, last_was_word_char = FALSE;
 
   PangoAnalysis *analyses;
 
@@ -435,10 +436,29 @@
     {
       PangoAnalysis *analysis = &analyses[i];
       PangoAnalysis *last_analysis = i > 0 ? &analyses[i-1] : 0;
+      gboolean need_new_item = FALSE;
       
       next = g_utf8_next_char (p);
       
-      if (i == 0 ||
+      if (word_underline_on)
+	{
+	  gboolean is_word_char;
+
+	  /* Check if we've just switched from a word char to a non-word
+	   * char, in which case we want to start a new run.
+	   */
+	  if (g_unichar_isgraph (g_utf8_get_char (p)))
+	    is_word_char = TRUE;
+	  else
+	    is_word_char = FALSE;
+
+	  if (is_word_char != last_was_word_char)
+	    need_new_item = TRUE;
+
+	  last_was_word_char = is_word_char;
+	}
+
+      if (i == 0 || need_new_item ||
 	  text_ucs4[i] == '\t' || text_ucs4[i-1] == '\t' ||
 	  embedding_levels[i] != embedding_levels[i-1] ||
 	  analysis->shape_engine != last_analysis->shape_engine ||
@@ -447,6 +467,8 @@
 	  analysis->language != last_analysis->language ||
 	  analysis->extra_attrs != last_analysis->extra_attrs)
 	{
+	  GSList *tmp_list;
+
           /* assert that previous item got at least one char */
           g_assert (item == NULL || item->length > 0);
           g_assert (item == NULL || item->num_chars > 0);
@@ -478,6 +500,32 @@
 	  else
 	    item->analysis.extra_attrs = analysis->extra_attrs;
 
+	  word_underline_on = FALSE;
+	  for (tmp_list = item->analysis.extra_attrs; tmp_list;
+	       tmp_list = tmp_list->next)
+	    {
+	      PangoAttribute *attr = tmp_list->data;
+
+	      if (attr->klass->type == PANGO_ATTR_UNDERLINE
+		  && ((PangoAttrInt *)attr)->value == PANGO_UNDERLINE_WORD)
+		{
+		  /* Set the flag to indicate word underlining is on, so we
+		   * look for word/non-word boundaries.
+		   */
+		  word_underline_on = TRUE;
+
+		  if (g_unichar_isgraph (g_utf8_get_char (p)))
+		    {
+		      last_was_word_char = TRUE;
+		    }
+		  else
+		    {
+		      last_was_word_char = FALSE;
+		      ((PangoAttrInt *)attr)->value = PANGO_UNDERLINE_NONE;
+		    }
+		}
+	    }
+
 	  result = g_list_prepend (result, item);
 	}
       else
--- pango-layout.c.orig	Mon Oct 14 23:50:47 2002
+++ pango-layout.c	Mon Oct 21 02:44:48 2002
@@ -3512,6 +3512,7 @@
     case PANGO_UNDERLINE_NONE:
       break;
     case PANGO_UNDERLINE_SINGLE:
+    case PANGO_UNDERLINE_WORD:
       if (run_ink)
         run_ink->height = MAX (run_ink->height, 2 * PANGO_SCALE - run_ink->y);
       if (run_logical)
--- pango-markup.c.orig	Tue Jul  2 18:15:22 2002
+++ pango-markup.c	Mon Oct 21 02:48:09 2002
@@ -1133,6 +1133,8 @@
         ul = PANGO_UNDERLINE_DOUBLE;
       else if (strcmp (underline, "low") == 0)
         ul = PANGO_UNDERLINE_LOW;
+      else if (strcmp (underline, "word") == 0)
+        ul = PANGO_UNDERLINE_WORD;
       else if (strcmp (underline, "none") == 0)
         ul = PANGO_UNDERLINE_NONE;
       else
--- pangox.c.orig	Mon Oct 14 23:50:47 2002
+++ pangox.c	Mon Oct 21 18:53:06 2002
@@ -1582,6 +1582,7 @@
 		     x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
 	  /* Fall through */
 	case PANGO_UNDERLINE_SINGLE:
+	case PANGO_UNDERLINE_WORD:
 	  XDrawLine (display, drawable, fg_gc,
 		     x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
 		     x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
--- pangoft2.c.orig	Mon Oct 21 18:53:57 2002
+++ pangoft2.c	Mon Oct 21 18:54:00 2002
@@ -895,6 +895,7 @@
 				x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width));
 	  /* Fall through */
 	case PANGO_UNDERLINE_SINGLE:
+	case PANGO_UNDERLINE_WORD:
 	  pango_ft2_draw_hline (bitmap, y + 2,
 				x + PANGO_PIXELS (x_off + ink_rect.x),
 				x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width));
--- pangowin32.c.orig	Mon Oct 14 23:50:47 2002
+++ pangowin32.c	Mon Oct 21 18:55:03 2002
@@ -676,6 +676,7 @@
 	  Polyline (hdc, points, 2);
 	  /* Fall through */
 	case PANGO_UNDERLINE_SINGLE:
+	case PANGO_UNDERLINE_WORD:
 	  points[0].y = points[1].y = y + 2;
 	  Polyline (hdc, points, 2);
 	  break;
--- gtktextdisplay.c.orig	Mon Oct 21 18:48:59 2002
+++ gtktextdisplay.c	Mon Oct 21 18:48:09 2002
@@ -258,6 +258,7 @@
       gint risen_y;
       gint shaped_width_pixels = 0;
       gboolean need_ink = FALSE;
+      PangoUnderline underline;
       
       tmp_list = tmp_list->next;
 
@@ -281,8 +282,21 @@
           fg_gc = render_state->fg_gc;
         }
       
-      if (appearance->underline != PANGO_UNDERLINE_NONE ||
-          appearance->strikethrough)
+      /* If word-underlining is used, check if this is a non-word run, in
+       * which case we don't display the underline. (Pango will already have
+       * made sure the text is split into word and non-word runs.)
+       */
+      underline = appearance->underline;
+      if (underline == PANGO_UNDERLINE_WORD && run->item->num_chars)
+	{
+	  const char *text = pango_layout_get_text (line->layout);
+	  gunichar ch= g_utf8_get_char (text + run->item->offset);
+
+	  if (!g_unichar_isgraph (ch))
+	    underline = PANGO_UNDERLINE_NONE;
+	}
+
+      if (underline != PANGO_UNDERLINE_NONE || appearance->strikethrough)
         need_ink = TRUE;
       
       if (appearance->is_text)
@@ -434,7 +448,7 @@
             g_assert_not_reached (); /* not a pixbuf or widget */
         }
 
-      switch (appearance->underline)
+      switch (underline)
         {
         case PANGO_UNDERLINE_NONE:
           break;
@@ -447,6 +461,7 @@
                          risen_y + 3);
           /* Fall through */
         case PANGO_UNDERLINE_SINGLE:
+        case PANGO_UNDERLINE_WORD:
           g_assert (need_ink);
           gdk_draw_line (drawable, fg_gc,
                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]