[gtk/wip/baedert/for-master: 48/48] label: Set underline text and markup in one step



commit 906e55de1b1651d50afa75e0d62c6f459457e20a
Author: Timm Bäder <mail baedert org>
Date:   Sun Apr 12 19:54:20 2020 +0200

    label: Set underline text and markup in one step
    
    Try to unify the way we parse the mnemonic character

 gtk/gtklabel.c | 364 ++++++++++++++++++++-------------------------------------
 1 file changed, 129 insertions(+), 235 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 794e08739b..7f417a0f50 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -465,8 +465,6 @@ static gboolean gtk_label_set_use_markup_internal    (GtkLabel  *label,
                                                       gboolean   val);
 static gboolean gtk_label_set_use_underline_internal (GtkLabel  *label,
                                                       gboolean   val);
-static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
-                                                 const gchar   *str);
 static void gtk_label_set_markup_internal        (GtkLabel      *label,
                                                  const gchar   *str,
                                                  gboolean       with_uline);
@@ -510,11 +508,6 @@ static void     gtk_label_buildable_custom_finished  (GtkBuildable       *builda
                                                       GObject            *child,
                                                       const gchar        *tagname,
                                                       gpointer            user_data);
-static gboolean      separate_uline_pattern     (const gchar  *str,
-                                                 guint        *accel_key,
-                                                 gchar       **new_str,
-                                                 gchar       **pattern);
-
 
 /* For selectable labels: */
 static void gtk_label_move_cursor        (GtkLabel        *label,
@@ -1457,7 +1450,6 @@ label_mnemonics_visible_changed (GtkWidget  *widget,
 {
   gboolean visible;
 
-  g_message (__FUNCTION__);
   g_object_get (widget, "mnemonics-visible", &visible, NULL);
   _gtk_label_mnemonics_visible_apply_recursively (widget, visible);
 }
@@ -1815,10 +1807,8 @@ gtk_label_recalculate (GtkLabel *label)
   gtk_label_clear_layout (label);
   gtk_label_clear_select_info (label);
 
-  if (priv->use_markup)
+  if (priv->use_markup || priv->use_underline)
     gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
-  else if (priv->use_underline)
-    gtk_label_set_uline_text_internal (label, priv->label);
   else
     {
       g_clear_pointer (&priv->markup_attrs, pango_attr_list_unref);
@@ -2264,6 +2254,79 @@ gtk_label_ensure_has_tooltip (GtkLabel *label)
   gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
 }
 
+/* Reads @text and extracts the accel key, if any.
+ * @new_text will be set to the given text with the first _ removed.
+ *
+ * Returned will be the one underline attribute used for the mnemonic
+ * */
+static void
+extract_mnemonic_keyval (const char      *text,
+                         guint           *out_accel_key,
+                         char           **out_new_text,
+                         PangoAttribute **out_mnemonic_attribute)
+{
+  const gsize text_len = strlen (text);
+  gunichar c;
+  const char *p;
+
+  p = text;
+  for (;;)
+    {
+      const char *_index;
+
+      c = g_utf8_get_char (p);
+
+      if (c == '\0')
+        break;
+
+      if (c != '_')
+        {
+          p = g_utf8_next_char (p);
+          continue;
+        }
+
+      _index = p;
+
+      p = g_utf8_next_char (p);
+      c = g_utf8_get_char (p);
+
+      if (c != '_' && c != '0')
+        {
+          const gsize byte_index = p - text - 1; /* Of the _ */
+
+          /* c is the accel key */
+          if (out_accel_key)
+            *out_accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
+          if (out_new_text)
+            {
+              *out_new_text = g_malloc (text_len);
+              memcpy (*out_new_text, text, byte_index);
+              memcpy (*out_new_text + byte_index, p, text_len - byte_index);
+            }
+
+          if (out_mnemonic_attribute)
+            {
+              PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+              attr->start_index = _index - text;
+              attr->end_index = p - text;
+              *out_mnemonic_attribute = attr;
+            }
+
+          return;
+        }
+
+      p = g_utf8_next_char (p);
+    }
+
+  /* No accel key found */
+  if (out_accel_key)
+    *out_accel_key = GDK_KEY_VoidSymbol;
+  if (out_new_text)
+    *out_new_text = NULL;
+  if (out_mnemonic_attribute)
+    *out_mnemonic_attribute = NULL;
+}
+
 static void
 gtk_label_set_markup_internal (GtkLabel    *label,
                                const gchar *str,
@@ -2273,20 +2336,13 @@ gtk_label_set_markup_internal (GtkLabel    *label,
   gchar *text = NULL;
   GError *error = NULL;
   PangoAttrList *attrs = NULL;
-  gunichar accel_char = 0;
   gchar *str_for_display = NULL;
-  gchar *str_for_accel = NULL;
   GtkLabelLink *links = NULL;
   guint n_links = 0;
+  PangoAttribute *mnemonic_attr = NULL;
 
   if (!parse_uri_markup (label, str, &str_for_display, &links, &n_links, &error))
-    {
-      g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
-                 str, error->message);
-      g_error_free (error);
-      return;
-    }
-
+    goto error_set;
 
   if (links)
     {
@@ -2298,68 +2354,57 @@ gtk_label_set_markup_internal (GtkLabel    *label,
       gtk_widget_add_css_class (GTK_WIDGET (label), "link");
     }
 
-  if (with_uline)
-    {
-      gboolean enable_mnemonics = TRUE;
+  if (!with_uline)
+   {
+no_uline:
+      /* Extract the text to display */
+      if (!pango_parse_markup (str_for_display, -1, 0, &attrs, &text, NULL, &error))
+        goto error_set;
+   }
+  else /* Underline AND markup is a little more complicated... */
+   {
+      char *new_text = NULL;
+      guint accel_keyval;
       gboolean auto_mnemonics = TRUE;
+      gboolean do_mnemonics = priv->mnemonics_visible &&
+                              (!auto_mnemonics || gtk_widget_is_sensitive (GTK_WIDGET (label))) &&
+                              (!priv->mnemonic_widget || gtk_widget_is_sensitive (priv->mnemonic_widget));
 
-      str_for_accel = g_strdup (str_for_display);
+      /* Remove the mnemonic underline */
+      extract_mnemonic_keyval (str_for_display,
+                               &accel_keyval,
+                               &new_text,
+                               NULL);
+      if (!new_text) /* No underline found anyway */
+        goto no_uline;
 
-      if (!(enable_mnemonics && priv->mnemonics_visible &&
-            (!auto_mnemonics ||
-             (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
-              (!priv->mnemonic_widget ||
-               gtk_widget_is_sensitive (priv->mnemonic_widget))))))
-        {
-          gchar *tmp;
-          gchar *pattern;
-          guint key;
+      priv->mnemonic_keyval = accel_keyval;
 
-          if (separate_uline_pattern (str_for_display, &key, &tmp, &pattern))
-            {
-              g_free (str_for_display);
-              str_for_display = tmp;
-              g_free (pattern);
-            }
-        }
-    }
+      /* Extract the text to display */
+      if (!pango_parse_markup (new_text, -1, '_', &attrs, &text, NULL, &error))
+        goto error_set;
 
-  /* Extract the text to display */
-  if (!pango_parse_markup (str_for_display,
-                           -1,
-                           with_uline ? '_' : 0,
-                           &attrs,
-                           &text,
-                           NULL,
-                           &error))
-    {
-      g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
-                 str_for_display, error->message);
-      g_free (str_for_display);
-      g_free (str_for_accel);
-      g_error_free (error);
-      return;
-    }
+      if (do_mnemonics)
+        {
+          /* text is now the final text, but we need to parse str_for_display once again
+           * *with* the mnemonic underline so we can remove the markup tags and get the
+           * proper attribute indices */
+          char *text_for_accel;
+
+          if (!pango_parse_markup (str_for_display, -1, 0, NULL, &text_for_accel, NULL, &error))
+            goto error_set;
+
+          extract_mnemonic_keyval (text_for_accel,
+                                   NULL,
+                                   NULL,
+                                   &mnemonic_attr);
+          g_free (text_for_accel);
+        }
 
-  /* Extract the accelerator character */
-  if (with_uline && !pango_parse_markup (str_for_accel,
-                                        -1,
-                                        '_',
-                                        NULL,
-                                        NULL,
-                                        &accel_char,
-                                        &error))
-    {
-      g_warning ("Failed to set text from markup due to error parsing markup: %s",
-                 error->message);
-      g_free (str_for_display);
-      g_free (str_for_accel);
-      g_error_free (error);
-      return;
-    }
+      g_free (new_text);
+   }
 
   g_free (str_for_display);
-  g_free (str_for_accel);
 
   if (text)
     gtk_label_set_text_internal (label, text);
@@ -2368,12 +2413,18 @@ gtk_label_set_markup_internal (GtkLabel    *label,
     {
       g_clear_pointer (&priv->markup_attrs, pango_attr_list_unref);
       priv->markup_attrs = attrs;
+
+      if (mnemonic_attr)
+        pango_attr_list_insert_before (priv->markup_attrs, mnemonic_attr);
     }
 
-  if (accel_char != 0)
-    priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
-  else
-    priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
+  return;
+
+error_set:
+  g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
+             str, error->message);
+  g_error_free (error);
+
 }
 
 /**
@@ -2478,47 +2529,6 @@ gtk_label_get_text (GtkLabel *label)
   return priv->text;
 }
 
-static PangoAttrList *
-gtk_label_pattern_to_attrs (GtkLabel      *label,
-                           const gchar   *pattern)
-{
-  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  const char *start;
-  const char *p = priv->text;
-  const char *q = pattern;
-  PangoAttrList *attrs;
-
-  attrs = pango_attr_list_new ();
-
-  while (1)
-    {
-      while (*p && *q && *q != '_')
-       {
-         p = g_utf8_next_char (p);
-         q++;
-       }
-      start = p;
-      while (*p && *q && *q == '_')
-       {
-         p = g_utf8_next_char (p);
-         q++;
-       }
-      
-      if (p > start)
-       {
-         PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
-         attr->start_index = start - priv->text;
-         attr->end_index = p - priv->text;
-         
-         pango_attr_list_insert (attrs, attr);
-       }
-      else
-       break;
-    }
-
-  return attrs;
-}
-
 /**
  * gtk_label_set_justify:
  * @label: a #GtkLabel
@@ -3616,122 +3626,6 @@ gtk_label_snapshot (GtkWidget   *widget,
     }
 }
 
-static gboolean
-separate_uline_pattern (const gchar  *str,
-                        guint        *accel_key,
-                        gchar       **new_str,
-                        gchar       **pattern)
-{
-  gboolean underscore;
-  const gchar *src;
-  gchar *dest;
-  gchar *pattern_dest;
-
-  *accel_key = GDK_KEY_VoidSymbol;
-  *new_str = g_new (gchar, strlen (str) + 1);
-  *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
-
-  underscore = FALSE;
-
-  src = str;
-  dest = *new_str;
-  pattern_dest = *pattern;
-
-  while (*src)
-    {
-      gunichar c;
-      const gchar *next_src;
-
-      c = g_utf8_get_char (src);
-      if (c == (gunichar)-1)
-       {
-         g_warning ("Invalid input string");
-         g_free (*new_str);
-         g_free (*pattern);
-
-         return FALSE;
-       }
-      next_src = g_utf8_next_char (src);
-
-      if (underscore)
-       {
-         if (c == '_')
-           *pattern_dest++ = ' ';
-         else
-           {
-             *pattern_dest++ = '_';
-             if (*accel_key == GDK_KEY_VoidSymbol)
-               *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
-           }
-
-         while (src < next_src)
-           *dest++ = *src++;
-
-         underscore = FALSE;
-       }
-      else
-       {
-         if (c == '_')
-           {
-             underscore = TRUE;
-             src = next_src;
-           }
-         else
-           {
-             while (src < next_src)
-               *dest++ = *src++;
-
-             *pattern_dest++ = ' ';
-           }
-       }
-    }
-
-  *dest = 0;
-  *pattern_dest = 0;
-
-  return TRUE;
-}
-
-static void
-gtk_label_set_uline_text_internal (GtkLabel   *label,
-                                   const char *str)
-{
-  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  guint accel_key = GDK_KEY_VoidSymbol;
-  gboolean auto_mnemonics = TRUE;
-  PangoAttrList *attrs;
-  char *new_str;
-  char *pattern;
-
-  g_return_if_fail (GTK_IS_LABEL (label));
-  g_return_if_fail (str != NULL);
-
-  /* Split text into the base text and a separate pattern
-   * of underscores.
-   */
-  if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
-    return;
-
-  gtk_label_set_text_internal (label, new_str);
-
-  if (priv->mnemonics_visible && pattern &&
-      (!auto_mnemonics ||
-       (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
-        (!priv->mnemonic_widget ||
-         gtk_widget_is_sensitive (priv->mnemonic_widget)))))
-    attrs = gtk_label_pattern_to_attrs (label, pattern);
-  else
-    attrs = NULL;
-
-  if (priv->markup_attrs)
-    pango_attr_list_unref (priv->markup_attrs);
-  priv->markup_attrs = attrs;
-
-  priv->mnemonic_keyval = accel_key;
-
-  g_free (pattern);
-}
-
 /**
  * gtk_label_set_text_with_mnemonic:
  * @label: a #GtkLabel


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