/cvs/xorg/xc/extras/fontconfig/fc-list A D/drivers//// A D/drivers//// A D/ibm//// A D/mfb//// A D/mi//// A D/misc//// A D/vga//// // A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/hw//// A D/ilbm//// A D/include//// A D/iplan2p2//// A D/iplan2p4//// A D/iplan2p8//// A D/lbx//// A D/lmfcfb//// A D/mfb//// A D/mfb//// A D/mi//// A D/miext//// A D/miext//// A D/miext//// A D/miext//// A D/miext//// A D/miext//// A D/miext//// A D/miext//// A D/os//// A D/oslbx//// A D/pandix//// A D/panmi//// A D/randr//// A D/record//// A D/record//// A D/render//// A D/xfixes//// A D/xkb//// /cvs/xorg/xc/programs/Xserver/hw/xfree98/vga16/drivers LL) + return; + + g_list_foreach(priv->links, (GFunc)g_free, NULL); + g_list_free(priv->links); + priv->links = NULL; +} + +static void +sexy_url_label_clear_urls(SexyUrlLabel *url_label) +{ + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + + if (priv->urls == NULL) + return; + + g_list_foreach(priv->urls, (GFunc)g_free, NULL); + g_list_free(priv->urls); + priv->urls = NULL; +} + +static void +sexy_url_label_rescan_label(SexyUrlLabel *url_label) +{ + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(url_label)); + PangoAttrList *list = pango_layout_get_attributes(layout); + PangoAttrIterator *iter; + gint layout_x, layout_y; + GList *url_list; + + sexy_url_label_clear_links(url_label); + + iter = pango_attr_list_get_iterator(list); + + gtk_label_get_layout_offsets(GTK_LABEL(url_label), &layout_x, &layout_y); + + layout_x -= GTK_WIDGET(url_label)->allocation.x; + layout_y -= GTK_WIDGET(url_label)->allocation.y; + + url_list = priv->urls; + + do + { + PangoAttribute *underline; + PangoAttribute *color; + + underline = pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE); + color = pango_attr_iterator_get(iter, PANGO_ATTR_FOREGROUND); + + if (underline != NULL && color != NULL) + { + gint start, end; + PangoRectangle start_pos; + PangoRectangle end_pos; + SexyUrlLabelLink *link; + + pango_attr_iterator_range(iter, &start, &end); + + pango_layout_index_to_pos(layout, start, &start_pos); + pango_layout_index_to_pos(layout, end, &end_pos); + + link = g_new0(SexyUrlLabelLink, 1); + link->x1 = layout_x + PANGO_PIXELS(start_pos.x); + link->y1 = layout_y + PANGO_PIXELS(start_pos.y); + link->x2 = layout_x + + PANGO_PIXELS(end_pos.x) + PANGO_PIXELS(end_pos.width); + link->y2 = layout_y + + PANGO_PIXELS(end_pos.y) + PANGO_PIXELS(end_pos.height); + + link->url = (const gchar *)url_list->data; + priv->links = g_list_append(priv->links, link); + + url_list = url_list->next; + } + + } while (pango_attr_iterator_next(iter)); +} + +static void +start_element_handler(GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + SexyUrlLabel *url_label = SEXY_URL_LABEL(user_data); + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + + if (!strcmp(element_name, "a")) + { + const gchar *url = NULL; + int line_number; + int char_number; + int i; + + g_markup_parse_context_get_position(context, &line_number, + &char_number); + + for (i = 0; attribute_names[i] != NULL; i++) + { + const gchar *attr = attribute_names[i]; + + if (!strcmp(attr, "href")) + { + if (url != NULL) + { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Attribute '%s' occurs twice on <a> tag " + "on line %d char %d, may only occur once", + attribute_names[i], line_number, char_number); + return; + } + + url = attribute_values[i]; + } + else + { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + "Attribute '%s' is not allowed on the <a> tag " + "on line %d char %d", + attribute_names[i], line_number, char_number); + return; + } + } + + if (url == NULL) + { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Attribute 'href' was missing on the <a> tag " + "on line %d char %d", + line_number, char_number); + return; + } + + g_string_append(priv->temp_markup_result, + "<span color=\"blue\" underline=\"single\">"); + + priv->urls = g_list_append(priv->urls, g_strdup(url)); + } + else + { + int i; + + g_string_append_printf(priv->temp_markup_result, + "<%s", element_name); + + for (i = 0; attribute_names[i] != NULL; i++) + { + const gchar *attr = attribute_names[i]; + const gchar *value = attribute_values[i]; + + g_string_append_printf(priv->temp_markup_result, + " %s=\"%s\"", + attr, value); + } + + g_string_append_c(priv->temp_markup_result, '>'); + } +} + +static void +end_element_handler(GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SexyUrlLabel *url_label = SEXY_URL_LABEL(user_data); + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + + if (!strcmp(element_name, "a")) + { + g_string_append(priv->temp_markup_result, "</span>"); + } + else + { + g_string_append_printf(priv->temp_markup_result, + "</%s>", element_name); + } +} + +static void +text_handler(GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SexyUrlLabel *url_label = SEXY_URL_LABEL(user_data); + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + + g_string_append_len(priv->temp_markup_result, text, text_len); +} + +static const GMarkupParser markup_parser = +{ + start_element_handler, + end_element_handler, + text_handler, + NULL, + NULL +}; + +static gboolean +xml_isspace(char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static gboolean +parse_custom_markup(SexyUrlLabel *url_label, const gchar *markup, + gchar **ret_markup) +{ + GMarkupParseContext *context = NULL; + SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); + GError *error = NULL; + const gchar *p, *end; + gboolean needs_root = TRUE; + gsize length; + + g_return_val_if_fail(markup != NULL, FALSE); + g_return_val_if_fail(ret_markup != NULL, FALSE); + + priv->temp_markup_result = g_string_new(NULL); + + length = strlen(markup); + p = markup; + end = markup + length; + + while (p != end && xml_isspace(*p)) + p++; + + if (end - p >= 8 && strncmp(p, "<markup>", 8) == 0) + needs_root = FALSE; + + context = g_markup_parse_context_new(&markup_parser, 0, url_label, NULL); + + if (needs_root) + { + if (!g_markup_parse_context_parse(context, "<markup>", -1, &error)) + goto failed; + } + + if (!g_markup_parse_context_parse(context, markup, strlen(markup), &error)) + goto failed; + + if (needs_root) + { + if (!g_markup_parse_context_parse(context, "</markup>", -1, &error)) + goto failed; + } + + if (!g_markup_parse_context_end_parse(context, &error)) + goto failed; + + if (error != NULL) + g_error_free(error); + + g_markup_parse_context_free(context); + + *ret_markup = g_string_free(priv->temp_markup_result, FALSE); + priv->temp_markup_result = NULL; + + return TRUE; + +failed: + fprintf(stderr, "Unable to parse markup: %s\n", error->message); + g_error_free(error); + + g_string_free(priv->temp_markup_result, TRUE); + priv->temp_markup_result = NULL; + + g_markup_parse_context_free(context); + return FALSE; +} + +void +sexy_url_label_set_markup(SexyUrlLabel *url_label, const gchar *markup) +{ + gchar *new_markup; + + g_return_if_fail(SEXY_IS_URL_LABEL(url_label)); + + sexy_url_label_clear_links(url_label); + sexy_url_label_clear_urls(url_label); + + if (markup == NULL || *markup == '\0') + { + gtk_label_set_markup(GTK_LABEL(url_label), ""); + return; + } + + if (parse_custom_markup(url_label, markup, &new_markup)) + { + gtk_label_set_markup(GTK_LABEL(url_label), new_markup); + } + else + { + gtk_label_set_markup(GTK_LABEL(url_label), ""); + } + + sexy_url_label_rescan_label(url_label); +} Added: trunk/notification-daemon/src/sexy-url-label.h =================================================================== --- trunk/notification-daemon/src/sexy-url-label.h 2005-06-25 03:57:08 UTC (rev 1982) +++ trunk/notification-daemon/src/sexy-url-label.h 2005-06-26 00:46:58 UTC (rev 1983) @@ -0,0 +1,74 @@ +/** + * @file libsexy/sexy-url-label.h URL Label + * + * @Copyright (C) 2005 Christian Hammond + * + * 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.1 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _SEXY_URL_LABEL_H_ +#define _SEXY_URL_LABEL_H_ + +typedef struct _SexyUrlLabel SexyUrlLabel; +typedef struct _SexyUrlLabelClass SexyUrlLabelClass; + +#include <gtk/gtklabel.h> + +#define SEXY_TYPE_URL_LABEL (sexy_url_label_get_type()) +#define SEXY_URL_LABEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_URL_LABEL, SexyUrlLabel)) +#define SEXY_URL_LABEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_URL_LABEL, SexyUrlLabelClass)) +#define SEXY_IS_URL_LABEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_URL_LABEL)) +#define SEXY_IS_URL_LABEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_URL_LABEL)) +#define SEXY_URL_LABEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_URL_LABEL, SexyUrlLabelClass)) + +struct _SexyUrlLabel +{ + GtkLabel parent_object; + + void (*gtk_reserved1)(void); + void (*gtk_reserved2)(void); + void (*gtk_reserved3)(void); + void (*gtk_reserved4)(void); +}; + +struct _SexyUrlLabelClass +{ + GtkLabelClass parent_class; + + /* Signals */ + void (*url_clicked)(SexyUrlLabel *url_label, const gchar *url); + + void (*gtk_reserved1)(void); + void (*gtk_reserved2)(void); + void (*gtk_reserved3)(void); + void (*gtk_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType sexy_url_label_get_type(void); + +GtkWidget *sexy_url_label_new(void); +GtkWidget *sexy_url_label_new_with_text(const gchar *text); +void sexy_url_label_set_markup(SexyUrlLabel *url_label, const gchar *markup); + +G_END_DECLS + +#endif /* _SEXY_URL_LABEL_H_ */


More information about the galago-commits mailing list