gtk+ r19995 - in trunk: . gtk tests



Author: tvb
Date: Fri Apr 11 17:56:26 2008
New Revision: 19995
URL: http://svn.gnome.org/viewvc/gtk+?rev=19995&view=rev

Log:

	* gtk/gtklabel.c: Added <attributes> / <attribute>
	custom tags to parse pango attributes into labels.

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



Modified:
   trunk/ChangeLog
   trunk/gtk/gtkbuildable.h
   trunk/gtk/gtklabel.c
   trunk/tests/buildertest.c

Modified: trunk/gtk/gtkbuildable.h
==============================================================================
--- trunk/gtk/gtkbuildable.h	(original)
+++ trunk/gtk/gtkbuildable.h	Fri Apr 11 17:56:26 2008
@@ -33,6 +33,7 @@
 #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;
 

Modified: trunk/gtk/gtklabel.c
==============================================================================
--- trunk/gtk/gtklabel.c	(original)
+++ trunk/gtk/gtklabel.c	Fri Apr 11 17:56:26 2008
@@ -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 @@
 					     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 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_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

Modified: trunk/tests/buildertest.c
==============================================================================
--- trunk/tests/buildertest.c	(original)
+++ trunk/tests/buildertest.c	Fri Apr 11 17:56:26 2008
@@ -1914,6 +1914,118 @@
 
 }
 
+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 @@
   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();
 }



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