]> Pileus Git - ~andy/gtk/commitdiff
Added <attributes> / <attribute> custom tags to parse pango attributes
authorTristan Van Berkom <tvb@src.gnome.org>
Fri, 11 Apr 2008 16:56:26 +0000 (16:56 +0000)
committerTristan Van Berkom <tvb@src.gnome.org>
Fri, 11 Apr 2008 16:56:26 +0000 (16:56 +0000)
* gtk/gtklabel.c: Added <attributes> / <attribute>
custom tags to parse pango attributes into labels.

* tests/buildertest.c: Added tests for GtkLabel custom
tag parsing.

svn path=/trunk/; revision=19995

ChangeLog
gtk/gtkbuildable.h
gtk/gtklabel.c
tests/buildertest.c

index 0b23a91b18046751875cf3a8ee338bb798580292..a229cd5acd0ef2e51dfd0284f48222b1e47b46fc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-04-11  Tristan Van Berkom <tvb@gnome.org>
+
+       * gtk/gtklabel.c: Added <attributes> / <attribute>
+       custom tags to parse pango attributes into labels.
+
+       * tests/buildertest.c: Added tests for GtkLabel custom
+       tag parsing.
+
 2008-04-10  Carlos Garnacho  <carlos@imendio.com>
 
        * gtk/gtknotebook.c (hide_drag_window): Do not call
index ee18fe8db5402487fa2ae44352fefdab627df71b..21fcc61147cc541841c8c0b664bbf4f837c90b21 100644 (file)
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
 #define GTK_IS_BUILDABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_BUILDABLE))
 #define GTK_BUILDABLE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_BUILDABLE, GtkBuildableIface))
 
+
 typedef struct _GtkBuildable      GtkBuildable; /* Dummy typedef */
 typedef struct _GtkBuildableIface GtkBuildableIface;
 
index 56bd2971658777a052cfc21889ea04d8fcaa5eff..eb71103c6348829b53fa3b6cb5eb03e6f31425ae 100644 (file)
@@ -43,6 +43,7 @@
 #include "gtknotebook.h"
 #include "gtkstock.h"
 #include "gtkbindings.h"
+#include "gtkbuildable.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -179,6 +180,20 @@ static void     gtk_label_drag_data_get     (GtkWidget         *widget,
                                             guint              info,
                                             guint              time);
 
+static void     gtk_label_buildable_interface_init     (GtkBuildableIface *iface);
+static gboolean gtk_label_buildable_custom_tag_start   (GtkBuildable     *buildable,
+                                                       GtkBuilder       *builder,
+                                                       GObject          *child,
+                                                       const gchar      *tagname,
+                                                       GMarkupParser    *parser,
+                                                       gpointer         *data);
+
+static void     gtk_label_buildable_custom_finished    (GtkBuildable     *buildable,
+                                                       GtkBuilder       *builder,
+                                                       GObject          *child,
+                                                       const gchar      *tagname,
+                                                       gpointer          user_data);
+
 
 /* For selectable lables: */
 static void gtk_label_move_cursor        (GtkLabel        *label,
@@ -197,7 +212,11 @@ static gint gtk_label_move_backward_word (GtkLabel        *label,
 
 static GQuark quark_angle = 0;
 
-G_DEFINE_TYPE (GtkLabel, gtk_label, GTK_TYPE_MISC)
+static GtkBuildableIface *buildable_parent_iface = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+                                               gtk_label_buildable_interface_init));
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -811,6 +830,332 @@ gtk_label_init (GtkLabel *label)
   gtk_label_set_text (label, "");
 }
 
+
+static void
+gtk_label_buildable_interface_init (GtkBuildableIface *iface)
+{
+  buildable_parent_iface = g_type_interface_peek_parent (iface);
+
+  iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
+  iface->custom_finished = gtk_label_buildable_custom_finished;
+}
+
+typedef struct {
+  GtkBuilder    *builder;
+  GObject       *object;
+  PangoAttrList *attrs;
+} PangoParserData;
+
+PangoAttribute *
+attribute_from_text (GtkBuilder   *builder,
+                    const gchar  *name, 
+                    const gchar  *value,
+                    GError      **error)
+{
+  PangoAttribute *attribute = NULL;
+  PangoAttrType   type;
+  PangoLanguage  *language;
+  PangoFontDescription *font_desc;
+  GdkColor       *color;
+  GValue          val = { 0, };
+
+  if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
+    return NULL;
+
+  type = g_value_get_enum (&val);
+  g_value_unset (&val);
+
+  switch (type)
+    {
+      /* PangoAttrLanguage */
+    case PANGO_ATTR_LANGUAGE:
+      if ((language = pango_language_from_string (value)))
+       {
+         attribute = pango_attr_language_new (language);
+         g_value_init (&val, G_TYPE_INT);
+       }
+      break;
+      /* PangoAttrInt */
+    case PANGO_ATTR_STYLE:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
+       attribute = pango_attr_style_new (g_value_get_enum (&val));
+      break;
+    case PANGO_ATTR_WEIGHT:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
+       attribute = pango_attr_weight_new (g_value_get_enum (&val));
+      break;
+    case PANGO_ATTR_VARIANT:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
+       attribute = pango_attr_variant_new (g_value_get_enum (&val));
+      break;
+    case PANGO_ATTR_STRETCH:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
+       attribute = pango_attr_stretch_new (g_value_get_enum (&val));
+      break;
+    case PANGO_ATTR_UNDERLINE:
+      if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
+       attribute = pango_attr_underline_new (g_value_get_boolean (&val));
+      break;
+    case PANGO_ATTR_STRIKETHROUGH:     
+      if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
+       attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
+      break;
+    case PANGO_ATTR_GRAVITY:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
+       attribute = pango_attr_gravity_new (g_value_get_enum (&val));
+      break;
+    case PANGO_ATTR_GRAVITY_HINT:
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT, 
+                                             value, &val, error))
+       attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
+      break;
+
+      /* PangoAttrString */      
+    case PANGO_ATTR_FAMILY:
+      attribute = pango_attr_family_new (value);
+      g_value_init (&val, G_TYPE_INT);
+      break;
+
+      /* PangoAttrSize */        
+    case PANGO_ATTR_SIZE:
+      if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, 
+                                             value, &val, error))
+       attribute = pango_attr_size_new (g_value_get_int (&val));
+      break;
+    case PANGO_ATTR_ABSOLUTE_SIZE:
+      if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, 
+                                             value, &val, error))
+       attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
+      break;
+    
+      /* PangoAttrFontDesc */
+    case PANGO_ATTR_FONT_DESC:
+      if ((font_desc = pango_font_description_from_string (value)))
+       {
+         attribute = pango_attr_font_desc_new (font_desc);
+         pango_font_description_free (font_desc);
+         g_value_init (&val, G_TYPE_INT);
+       }
+      break;
+
+      /* PangoAttrColor */
+    case PANGO_ATTR_FOREGROUND:
+      if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
+                                             value, &val, error))
+       {
+         color = g_value_get_boxed (&val);
+         attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
+       }
+      break;
+    case PANGO_ATTR_BACKGROUND: 
+      if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
+                                             value, &val, error))
+       {
+         color = g_value_get_boxed (&val);
+         attribute = pango_attr_background_new (color->red, color->green, color->blue);
+       }
+      break;
+    case PANGO_ATTR_UNDERLINE_COLOR:
+      if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
+                                             value, &val, error))
+       {
+         color = g_value_get_boxed (&val);
+         attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
+       }
+      break;
+    case PANGO_ATTR_STRIKETHROUGH_COLOR:
+      if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, 
+                                             value, &val, error))
+       {
+         color = g_value_get_boxed (&val);
+         attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
+       }
+      break;
+      
+      /* PangoAttrShape */
+    case PANGO_ATTR_SHAPE:
+      /* Unsupported for now */
+      break;
+      /* PangoAttrFloat */
+    case PANGO_ATTR_SCALE:
+      if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, 
+                                             value, &val, error))
+       attribute = pango_attr_scale_new (g_value_get_double (&val));
+      break;
+
+    case PANGO_ATTR_INVALID:
+    case PANGO_ATTR_LETTER_SPACING:
+    case PANGO_ATTR_RISE:
+    case PANGO_ATTR_FALLBACK:
+    default:
+      break;
+    }
+
+  g_value_unset (&val);
+
+  return attribute;
+}
+
+
+static void
+pango_start_element (GMarkupParseContext *context,
+                    const gchar         *element_name,
+                    const gchar        **names,
+                    const gchar        **values,
+                    gpointer             user_data,
+                    GError             **error)
+{
+  PangoParserData *data = (PangoParserData*)user_data;
+  GValue val = { 0, };
+  guint i;
+  gint line_number, char_number;
+
+  if (strcmp (element_name, "attribute") == 0)
+    {
+      PangoAttribute *attr = NULL;
+      const gchar *name = NULL;
+      const gchar *value = NULL;
+      const gchar *start = NULL;
+      const gchar *end = NULL;
+      guint start_val = 0;
+      guint end_val   = G_MAXUINT;
+
+      for (i = 0; names[i]; i++)
+       {
+         if (strcmp (names[i], "name") == 0)
+           name = values[i];
+         else if (strcmp (names[i], "value") == 0)
+           value = values[i];
+         else if (strcmp (names[i], "start") == 0)
+           start = values[i];
+         else if (strcmp (names[i], "end") == 0)
+           end = values[i];
+         else
+           {
+             g_markup_parse_context_get_position (context,
+                                                  &line_number,
+                                                  &char_number);
+             g_set_error (error,
+                          GTK_BUILDER_ERROR,
+                          GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
+                          "%s:%d:%d '%s' is not a valid attribute of <%s>",
+                          "<input>",
+                          line_number, char_number, names[i], "attribute");
+             return;
+           }
+       }
+
+      if (!name || !value)
+       {
+         g_markup_parse_context_get_position (context,
+                                              &line_number,
+                                              &char_number);
+         g_set_error (error,
+                      GTK_BUILDER_ERROR,
+                      GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
+                      "%s:%d:%d <%s> requires attribute \"%s\"",
+                      "<input>",
+                      line_number, char_number, "attribute",
+                      name ? "value" : "name");
+         return;
+       }
+
+      if (start)
+       {
+         if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, 
+                                                  start, &val, error))
+           return;
+         start_val = g_value_get_uint (&val);
+         g_value_unset (&val);
+       }
+
+      if (end)
+       {
+         if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, 
+                                                  end, &val, error))
+           return;
+         end_val = g_value_get_uint (&val);
+         g_value_unset (&val);
+       }
+
+      attr = attribute_from_text (data->builder, name, value, error);
+      attr->start_index = start_val;
+      attr->end_index   = end_val;
+
+      if (attr)
+       {
+         if (!data->attrs)
+           data->attrs = pango_attr_list_new ();
+
+         pango_attr_list_insert (data->attrs, attr);
+       }
+    }
+  else if (strcmp (element_name, "attributes") == 0)
+    ;
+  else
+    g_warning ("Unsupported tag for GtkLabel: %s\n", element_name);
+}
+
+static const GMarkupParser pango_parser =
+  {
+    pango_start_element,
+  };
+
+static gboolean
+gtk_label_buildable_custom_tag_start (GtkBuildable     *buildable,
+                                     GtkBuilder       *builder,
+                                     GObject          *child,
+                                     const gchar      *tagname,
+                                     GMarkupParser    *parser,
+                                     gpointer         *data)
+{
+  if (buildable_parent_iface->custom_tag_start (buildable, builder, child, 
+                                               tagname, parser, data))
+    return TRUE;
+
+  if (strcmp (tagname, "attributes") == 0)
+    {
+      PangoParserData *parser_data;
+
+      parser_data = g_slice_new0 (PangoParserData);
+      parser_data->builder = g_object_ref (builder);
+      parser_data->object = g_object_ref (buildable);
+      *parser = pango_parser;
+      *data = parser_data;
+      return TRUE;
+    }
+  return FALSE;
+}
+
+static void
+gtk_label_buildable_custom_finished (GtkBuildable *buildable,
+                                    GtkBuilder   *builder,
+                                    GObject      *child,
+                                    const gchar  *tagname,
+                                    gpointer      user_data)
+{
+  PangoParserData *data;
+
+  buildable_parent_iface->custom_finished (buildable, builder, child, 
+                                          tagname, user_data);
+
+  if (strcmp (tagname, "attributes") == 0)
+    {
+      data = (PangoParserData*)user_data;
+
+      if (data->attrs)
+       {
+         gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
+         pango_attr_list_unref (data->attrs);
+       }
+
+      g_object_unref (data->object);
+      g_object_unref (data->builder);
+      g_slice_free (PangoParserData, data);
+    }
+}
+
+
 /**
  * gtk_label_new:
  * @str: The text of the label
index f51fb1c5afa7097d11cfa001114dbe493efb8f0a..45558eca54ad167d6385484739eebcfc3324f23b 100644 (file)
@@ -1914,6 +1914,118 @@ test_icon_factory (void)
 
 }
 
+typedef struct {
+  gboolean weight;
+  gboolean foreground;
+  gboolean underline;
+  gboolean size;
+  gboolean font_desc;
+  gboolean language;
+} FoundAttrs;
+
+static gboolean 
+filter_pango_attrs (PangoAttribute *attr, 
+                   gpointer        data)
+{
+  FoundAttrs *found = (FoundAttrs *)data;
+
+  if (attr->klass->type == PANGO_ATTR_WEIGHT)
+    found->weight = TRUE;
+  else if (attr->klass->type == PANGO_ATTR_FOREGROUND)
+    found->foreground = TRUE;
+  else if (attr->klass->type == PANGO_ATTR_UNDERLINE)
+    found->underline = TRUE;
+  /* Make sure optional start/end properties are working */
+  else if (attr->klass->type == PANGO_ATTR_SIZE && 
+          attr->start_index == 5 &&
+          attr->end_index   == 10)
+    found->size = TRUE;
+  else if (attr->klass->type == PANGO_ATTR_FONT_DESC)
+    found->font_desc = TRUE;
+  else if (attr->klass->type == PANGO_ATTR_LANGUAGE)
+    found->language = TRUE;
+
+  return TRUE;
+}
+
+static void
+test_pango_attributes (void)
+{
+  GtkBuilder *builder;
+  FoundAttrs found = { 0, };
+  const gchar buffer[] =
+    "<interface>"
+    "  <object class=\"GtkLabel\" id=\"label1\">"
+    "    <attributes>"
+    "      <attribute name=\"weight\" value=\"PANGO_WEIGHT_BOLD\"/>"
+    "      <attribute name=\"foreground\" value=\"DarkSlateGray\"/>"
+    "      <attribute name=\"underline\" value=\"True\"/>"
+    "      <attribute name=\"size\" value=\"4\" start=\"5\" end=\"10\"/>"
+    "      <attribute name=\"font-desc\" value=\"Sans Italic 22\"/>"
+    "      <attribute name=\"language\" value=\"pt_BR\"/>"
+    "    </attributes>"
+    "  </object>"
+    "</interface>";
+  const gchar err_buffer1[] =
+    "<interface>"
+    "  <object class=\"GtkLabel\" id=\"label1\">"
+    "    <attributes>"
+    "      <attribute name=\"weight\"/>"
+    "    </attributes>"
+    "  </object>"
+    "</interface>";
+  const gchar err_buffer2[] =
+    "<interface>"
+    "  <object class=\"GtkLabel\" id=\"label1\">"
+    "    <attributes>"
+    "      <attribute name=\"weight\" value=\"PANGO_WEIGHT_BOLD\" unrecognized=\"True\"/>"
+    "    </attributes>"
+    "  </object>"
+    "</interface>";
+
+  GObject *label;
+  GError  *error = NULL;
+  PangoAttrList *attrs, *filtered;
+  
+  /* Test attributes are set */
+  builder = builder_new_from_string (buffer, -1, NULL);
+  label = gtk_builder_get_object (builder, "label1");
+  g_assert (label != NULL);
+
+  attrs = gtk_label_get_attributes (GTK_LABEL (label));
+  g_assert (attrs != NULL);
+
+  filtered = pango_attr_list_filter (attrs, filter_pango_attrs, &found);
+  g_assert (filtered);
+  pango_attr_list_unref (filtered);
+
+  g_assert (found.weight);
+  g_assert (found.foreground);
+  g_assert (found.underline);
+  g_assert (found.size);
+  g_assert (found.language);
+  g_assert (found.font_desc);
+
+  g_object_unref (builder);
+
+  /* Test errors are set */
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_string (builder, err_buffer1, -1, &error);
+  label = gtk_builder_get_object (builder, "label1");
+  g_assert (error);
+  g_object_unref (builder);
+  g_error_free (error);
+  error = NULL;
+
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_string (builder, err_buffer2, -1, &error);
+  label = gtk_builder_get_object (builder, "label1");
+  g_assert (error);
+  g_object_unref (builder);
+  g_error_free (error);
+
+}
+
 static void 
 test_file (const gchar *filename)
 {
@@ -1996,6 +2108,7 @@ main (int argc, char **argv)
   g_test_add_func ("/Builder/Reference Counting", test_reference_counting);
   g_test_add_func ("/Builder/Window", test_window);
   g_test_add_func ("/Builder/IconFactory", test_icon_factory);
+  g_test_add_func ("/Builder/PangoAttributes", test_pango_attributes);
 
   return g_test_run();
 }