/* gtkpango.c - pango-related utilities * * Copyright (c) 2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see .Free */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include "gtkpangoprivate.h" #include #include "gtkbuilderprivate.h" static gboolean attr_list_merge_filter (PangoAttribute *attribute, gpointer list) { pango_attr_list_change (list, pango_attribute_copy (attribute)); return FALSE; } /* * _gtk_pango_attr_list_merge: * @into: (nullable): a `PangoAttrList` where attributes are merged * @from: (nullable): a `PangoAttrList` with the attributes to merge * * Merges attributes from @from into @into. * * Returns: the merged list. */ PangoAttrList * _gtk_pango_attr_list_merge (PangoAttrList *into, PangoAttrList *from) { if (from) { if (into) pango_attr_list_filter (from, attr_list_merge_filter, into); else return pango_attr_list_ref (from); } return into; } static PangoAttribute * attribute_from_text (GtkBuilder *builder, const char *name, const char *value, GError **error) { PangoAttribute *attribute = NULL; PangoAttrType type; PangoLanguage *language; PangoFontDescription *font_desc; GdkRGBA *color; GValue val = G_VALUE_INIT; 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, PANGO_TYPE_UNDERLINE, value, &val, NULL)) attribute = pango_attr_underline_new (g_value_get_enum (&val)); else { /* XXX: allow boolean for backwards compat, so ignore error */ /* Deprecate this somehow */ g_value_unset (&val); 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_RGBA, value, &val, error)) { color = g_value_get_boxed (&val); attribute = pango_attr_foreground_new (color->red * 65535, color->green * 65535, color->blue * 65535); } break; case PANGO_ATTR_BACKGROUND: if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, value, &val, error)) { color = g_value_get_boxed (&val); attribute = pango_attr_background_new (color->red * 65535, color->green * 65535, color->blue * 65535); } break; case PANGO_ATTR_UNDERLINE_COLOR: if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, value, &val, error)) { color = g_value_get_boxed (&val); attribute = pango_attr_underline_color_new (color->red * 65535, color->green * 65535, color->blue * 65535); } break; case PANGO_ATTR_STRIKETHROUGH_COLOR: if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, value, &val, error)) { color = g_value_get_boxed (&val); attribute = pango_attr_strikethrough_color_new (color->red * 65535, color->green * 65535, color->blue * 65535); } 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_LETTER_SPACING: if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error)) attribute = pango_attr_letter_spacing_new (g_value_get_int (&val)); break; case PANGO_ATTR_RISE: if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error)) attribute = pango_attr_rise_new (g_value_get_int (&val)); break; case PANGO_ATTR_FALLBACK: if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error)) attribute = pango_attr_fallback_new (g_value_get_boolean (&val)); break; case PANGO_ATTR_FONT_FEATURES: attribute = pango_attr_font_features_new (value); break; case PANGO_ATTR_FOREGROUND_ALPHA: if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error)) attribute = pango_attr_foreground_alpha_new ((guint16)g_value_get_int (&val)); break; case PANGO_ATTR_BACKGROUND_ALPHA: if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error)) attribute = pango_attr_background_alpha_new ((guint16)g_value_get_int (&val)); break; case PANGO_ATTR_ALLOW_BREAKS: if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error)) attribute = pango_attr_allow_breaks_new (g_value_get_boolean (&val)); break; case PANGO_ATTR_SHOW: if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_SHOW_FLAGS, value, &val, error)) attribute = pango_attr_show_new (g_value_get_flags (&val)); break; case PANGO_ATTR_INSERT_HYPHENS: if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error)) attribute = pango_attr_insert_hyphens_new (g_value_get_boolean (&val)); break; case PANGO_ATTR_OVERLINE: if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_OVERLINE, value, &val, NULL)) attribute = pango_attr_overline_new (g_value_get_enum (&val)); break; case PANGO_ATTR_OVERLINE_COLOR: if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, value, &val, error)) { color = g_value_get_boxed (&val); attribute = pango_attr_overline_color_new (color->red * 65535, color->green * 65535, color->blue * 65535); } break; case PANGO_ATTR_LINE_HEIGHT: if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, value, &val, error)) attribute = pango_attr_line_height_new (g_value_get_double (&val)); break; case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error)) attribute = pango_attr_line_height_new_absolute (g_value_get_int (&val) * PANGO_SCALE); break; case PANGO_ATTR_TEXT_TRANSFORM: if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_TEXT_TRANSFORM, value, &val, error)) attribute = pango_attr_text_transform_new (g_value_get_enum (&val)); break; case PANGO_ATTR_WORD: attribute = pango_attr_word_new (); break; case PANGO_ATTR_SENTENCE: attribute = pango_attr_sentence_new (); break; case PANGO_ATTR_BASELINE_SHIFT: if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_BASELINE_SHIFT, value, &val, NULL)) attribute = pango_attr_baseline_shift_new (g_value_get_enum (&val)); else if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, NULL)) attribute = pango_attr_baseline_shift_new (g_value_get_enum (&val)); else g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, "Could not parse '%s' as baseline shift value", value); break; case PANGO_ATTR_FONT_SCALE: if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_FONT_SCALE, value, &val, error)) attribute = pango_attr_font_scale_new (g_value_get_enum (&val)); break; case PANGO_ATTR_INVALID: default: break; } g_value_unset (&val); return attribute; } void gtk_pango_attribute_start_element (GtkBuildableParseContext *context, const char *element_name, const char **names, const char **values, gpointer user_data, GError **error) { GtkPangoAttributeParserData *data = user_data; if (strcmp (element_name, "attribute") == 0) { PangoAttribute *attr = NULL; const char *name = NULL; const char *value = NULL; const char *start = NULL; const char *end = NULL; guint start_val = 0; guint end_val = G_MAXUINT; GValue val = G_VALUE_INIT; if (!_gtk_builder_check_parent (data->builder, context, "attributes", error)) return; if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_STRING, "value", &value, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "start", &start, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "end", &end, G_MARKUP_COLLECT_INVALID)) { _gtk_builder_prefix_error (data->builder, context, error); return; } if (start) { if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, start, &val, error)) { _gtk_builder_prefix_error (data->builder, context, 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)) { _gtk_builder_prefix_error (data->builder, context, error); return; } end_val = g_value_get_uint (&val); g_value_unset (&val); } attr = attribute_from_text (data->builder, name, value, error); if (!attr) { _gtk_builder_prefix_error (data->builder, context, error); return; } attr->start_index = start_val; attr->end_index = end_val; if (!data->attrs) data->attrs = pango_attr_list_new (); pango_attr_list_insert (data->attrs, attr); } else if (strcmp (element_name, "attributes") == 0) { if (!_gtk_builder_check_parent (data->builder, context, "object", error)) return; if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_INVALID, NULL, NULL, G_MARKUP_COLLECT_INVALID)) _gtk_builder_prefix_error (data->builder, context, error); } else { _gtk_builder_error_unhandled_tag (data->builder, context, "GtkWidget", element_name, error); } } const char * pango_style_to_string (PangoStyle style) { switch (style) { case PANGO_STYLE_NORMAL: return "normal"; case PANGO_STYLE_OBLIQUE: return "oblique"; case PANGO_STYLE_ITALIC: return "italic"; default: g_assert_not_reached (); } } const char * pango_variant_to_string (PangoVariant variant) { switch (variant) { case PANGO_VARIANT_NORMAL: return "normal"; case PANGO_VARIANT_SMALL_CAPS: return "small_caps"; case PANGO_VARIANT_ALL_SMALL_CAPS: return "all_small_caps"; case PANGO_VARIANT_PETITE_CAPS: return "petite_caps"; case PANGO_VARIANT_ALL_PETITE_CAPS: return "all_petite_caps"; case PANGO_VARIANT_UNICASE: return "unicase"; case PANGO_VARIANT_TITLE_CAPS: return "title_caps"; default: g_assert_not_reached (); } } const char * pango_stretch_to_string (PangoStretch stretch) { switch (stretch) { case PANGO_STRETCH_ULTRA_CONDENSED: return "ultra_condensed"; case PANGO_STRETCH_EXTRA_CONDENSED: return "extra_condensed"; case PANGO_STRETCH_CONDENSED: return "condensed"; case PANGO_STRETCH_SEMI_CONDENSED: return "semi_condensed"; case PANGO_STRETCH_NORMAL: return "normal"; case PANGO_STRETCH_SEMI_EXPANDED: return "semi_expanded"; case PANGO_STRETCH_EXPANDED: return "expanded"; case PANGO_STRETCH_EXTRA_EXPANDED: return "extra_expanded"; case PANGO_STRETCH_ULTRA_EXPANDED: return "ultra_expanded"; default: g_assert_not_reached (); } } const char * pango_underline_to_string (PangoUnderline value) { switch (value) { case PANGO_UNDERLINE_NONE: return "none"; case PANGO_UNDERLINE_SINGLE: case PANGO_UNDERLINE_SINGLE_LINE: return "single"; case PANGO_UNDERLINE_DOUBLE: case PANGO_UNDERLINE_DOUBLE_LINE: return "double"; case PANGO_UNDERLINE_LOW: return "low"; case PANGO_UNDERLINE_ERROR: case PANGO_UNDERLINE_ERROR_LINE: return "error"; default: g_assert_not_reached (); } } const char * pango_wrap_mode_to_string (PangoWrapMode mode) { /* Keep these in sync with gtk_wrap_mode_to_string() */ switch (mode) { case PANGO_WRAP_WORD: return "word"; case PANGO_WRAP_CHAR: return "char"; case PANGO_WRAP_WORD_CHAR: return "word-char"; default: g_assert_not_reached (); } } const char * pango_align_to_string (PangoAlignment align) { switch (align) { case PANGO_ALIGN_LEFT: return "left"; case PANGO_ALIGN_CENTER: return "center"; case PANGO_ALIGN_RIGHT: return "right"; default: g_assert_not_reached (); } }