forked from AuroraMiddleware/gtk
Added <attributes> / <attribute> custom tags to parse pango attributes
* 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
This commit is contained in:
parent
a011f8c06f
commit
58f712f7ed
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
347
gtk/gtklabel.c
347
gtk/gtklabel.c
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user