/* * @file libsexy/sexy-url-label.c URL Label * * @Copyright (C) 2005-2006 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. */ #include #include #include #include #define SEXY_URL_LABEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), SEXY_TYPE_URL_LABEL, \ SexyUrlLabelPrivate)) typedef struct { int start; int end; const gchar *url; } SexyUrlLabelLink; typedef struct { GList *links; GList *urls; SexyUrlLabelLink *active_link; GtkWidget *popup_menu; GdkWindow *event_window; int layout_x; int layout_y; size_t wrap_width; GString *temp_markup_result; } SexyUrlLabelPrivate; /* * NOTE: This *MUST* match the LabelWrapWidth struct in gtklabel.c. */ typedef struct { gint width; PangoFontDescription *font_desc; } LabelWrapWidth; enum { URL_ACTIVATED, LAST_SIGNAL }; static void sexy_url_label_finalize(GObject *obj); static void sexy_url_label_realize(GtkWidget *widget); static void sexy_url_label_unrealize(GtkWidget *widget); static void sexy_url_label_map(GtkWidget *widget); static void sexy_url_label_unmap(GtkWidget *widget); static void sexy_url_label_size_allocate(GtkWidget *widget, GtkAllocation *allocation); static gboolean sexy_url_label_motion_notify_event(GtkWidget *widget, GdkEventMotion *event); static gboolean sexy_url_label_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event); static gboolean sexy_url_label_button_press_event(GtkWidget *widget, GdkEventButton *event); static void open_link_activate_cb(GtkMenuItem *menu_item, SexyUrlLabel *url_label); static void copy_link_activate_cb(GtkMenuItem *menu_item, SexyUrlLabel *url_label); static void sexy_url_label_clear_links(SexyUrlLabel *url_label); static void sexy_url_label_clear_urls(SexyUrlLabel *url_label); static void sexy_url_label_rescan_label(SexyUrlLabel *url_label); static GtkLabelClass *parent_class = NULL; static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE(SexyUrlLabel, sexy_url_label, GTK_TYPE_LABEL); static void sexy_url_label_class_init(SexyUrlLabelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); parent_class = g_type_class_peek_parent(klass); object_class->finalize = sexy_url_label_finalize; widget_class->realize = sexy_url_label_realize; widget_class->unrealize = sexy_url_label_unrealize; widget_class->map = sexy_url_label_map; widget_class->unmap = sexy_url_label_unmap; widget_class->size_allocate = sexy_url_label_size_allocate; widget_class->motion_notify_event = sexy_url_label_motion_notify_event; widget_class->leave_notify_event = sexy_url_label_leave_notify_event; widget_class->button_press_event = sexy_url_label_button_press_event; g_type_class_add_private(klass, sizeof(SexyUrlLabelPrivate)); /** * SexyUrlLabel::url-activated: * @url_label: The label on which the signal was emitted. * @url: The URL which was activated. * * The ::url-activated signal is emitted when a URL in the label was * clicked. */ signals[URL_ACTIVATED] = g_signal_new("url_activated", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(SexyUrlLabelClass, url_activated), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); } static void selectable_changed_cb(SexyUrlLabel *url_label) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->event_window != NULL) { GdkCursor *cursor = NULL; if (gtk_label_get_selectable(GTK_LABEL(url_label)) && priv->active_link == NULL) cursor = gdk_cursor_new_for_display( gtk_widget_get_display(GTK_WIDGET(url_label)), GDK_XTERM); gdk_window_set_cursor(priv->event_window, cursor); if (cursor) gdk_cursor_unref(cursor); /* * GtkLabel recreates its event window when the selectable property * changes, which will cover ours. */ gdk_window_raise(priv->event_window); } } static void sexy_url_label_init(SexyUrlLabel *url_label) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); GtkWidget *item; GtkWidget *image; priv->links = NULL; priv->active_link = NULL; priv->event_window = NULL; g_signal_connect(G_OBJECT(url_label), "notify::selectable", G_CALLBACK(selectable_changed_cb), NULL); priv->popup_menu = gtk_menu_new(); /* Open Link */ item = gtk_image_menu_item_new_with_mnemonic("_Open Link"); gtk_widget_show(item); gtk_menu_shell_append(GTK_MENU_SHELL(priv->popup_menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(open_link_activate_cb), url_label); image = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); gtk_widget_show(image); /* Copy Link Address */ item = gtk_image_menu_item_new_with_mnemonic("Copy _Link Address"); gtk_widget_show(item); gtk_menu_shell_append(GTK_MENU_SHELL(priv->popup_menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(copy_link_activate_cb), url_label); image = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); gtk_widget_show(image); } static void sexy_url_label_finalize(GObject *obj) { SexyUrlLabel *url_label = SEXY_URL_LABEL(obj); sexy_url_label_clear_links(url_label); sexy_url_label_clear_urls(url_label); if (G_OBJECT_CLASS(parent_class)->finalize != NULL) G_OBJECT_CLASS(parent_class)->finalize(obj); } static gboolean sexy_url_label_motion_notify_event(GtkWidget *widget, GdkEventMotion *event) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(url_label)); GdkModifierType state; gboolean found = FALSE; GList *l; int index, trailing; int x, y; SexyUrlLabelLink *link = NULL; if (event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } if (pango_layout_xy_to_index(layout, (x - priv->layout_x) * PANGO_SCALE, (y - priv->layout_y) * PANGO_SCALE, &index, &trailing)) { for (l = priv->links; l != NULL; l = l->next) { link = (SexyUrlLabelLink *)l->data; if (index >= link->start && index <= link->end) { found = TRUE; break; } } } if (found) { if (priv->active_link == NULL) { GdkCursor *cursor; cursor = gdk_cursor_new_for_display( gtk_widget_get_display(widget), GDK_HAND2); gdk_window_set_cursor(priv->event_window, cursor); gdk_cursor_unref(cursor); priv->active_link = link; } } else { if (priv->active_link != NULL) { if (gtk_label_get_selectable(GTK_LABEL(url_label))) { GdkCursor *cursor; cursor = gdk_cursor_new_for_display( gtk_widget_get_display(widget), GDK_XTERM); gdk_window_set_cursor(priv->event_window, cursor); gdk_cursor_unref(cursor); } else gdk_window_set_cursor(priv->event_window, NULL); priv->active_link = NULL; } } /* * Another beautiful libsexy hack. This one prevents the callback * from going "Oh boy, they clicked and dragged! Let's select more of * the text!" */ if (priv->active_link != NULL) event->state = 0; GTK_WIDGET_CLASS(parent_class)->motion_notify_event(widget, event); return FALSE; } static gboolean sexy_url_label_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (GTK_WIDGET_CLASS(parent_class)->leave_notify_event != NULL) GTK_WIDGET_CLASS(parent_class)->leave_notify_event(widget, event); if (event->mode == GDK_CROSSING_NORMAL) { GdkCursor *cursor = NULL; if (gtk_label_get_selectable(GTK_LABEL(widget))) cursor = gdk_cursor_new_for_display( gtk_widget_get_display(widget), GDK_XTERM); gdk_window_set_cursor(priv->event_window, cursor); if (cursor) gdk_cursor_unref(cursor); priv->active_link = NULL; } return FALSE; } static gboolean sexy_url_label_button_press_event(GtkWidget *widget, GdkEventButton *event) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->active_link == NULL) { return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget, event); } if (event->button == 1) { g_signal_emit(url_label, signals[URL_ACTIVATED], 0, priv->active_link->url); } else if (event->button == 3) { gtk_menu_popup(GTK_MENU(priv->popup_menu), NULL, NULL, NULL, NULL, event->button, event->time); } return TRUE; } static void update_wrap_width(SexyUrlLabel *url_label, size_t wrap_width) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); LabelWrapWidth *wrap_width_data; GtkStyle *style; if (wrap_width == 0 || !gtk_label_get_line_wrap(GTK_LABEL(url_label))) return; #if 0 pango_layout_set_width(gtk_label_get_layout(GTK_LABEL(url_label)), wrap_width * PANGO_SCALE); #endif style = GTK_WIDGET(url_label)->style; wrap_width_data = g_object_get_data(G_OBJECT(style), "gtk-label-wrap-width"); if (wrap_width_data != NULL && wrap_width_data->width != wrap_width * PANGO_SCALE) { wrap_width_data->width = wrap_width * PANGO_SCALE; priv->wrap_width = wrap_width; g_object_unref(GTK_LABEL(url_label)->layout); GTK_LABEL(url_label)->layout = NULL; gtk_label_get_layout(GTK_LABEL(url_label)); gtk_widget_queue_resize(GTK_WIDGET(url_label)); } } static void sexy_url_label_realize(GtkWidget *widget) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); GdkWindowAttr attributes; gint attributes_mask; GTK_WIDGET_CLASS(parent_class)->realize(widget); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.window_type = GDK_WINDOW_CHILD; attributes.wclass = GDK_INPUT_ONLY; attributes.event_mask = gtk_widget_get_events(widget); attributes.event_mask |= (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); attributes_mask = GDK_WA_X | GDK_WA_Y; if (gtk_label_get_selectable(GTK_LABEL(widget))) { attributes.cursor = gdk_cursor_new_for_display( gtk_widget_get_display(widget), GDK_XTERM); attributes_mask |= GDK_WA_CURSOR; } priv->event_window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask); gdk_window_set_user_data(priv->event_window, widget); if (attributes_mask & GDK_WA_CURSOR) gdk_cursor_unref (attributes.cursor); } static void sexy_url_label_unrealize(GtkWidget *widget) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->event_window != NULL) { gdk_window_set_user_data(priv->event_window, NULL); gdk_window_destroy(priv->event_window); priv->event_window = NULL; } GTK_WIDGET_CLASS(parent_class)->unrealize(widget); } static void sexy_url_label_map(GtkWidget *widget) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); GTK_WIDGET_CLASS(parent_class)->map(widget); if (priv->event_window != NULL) gdk_window_show(priv->event_window); } static void sexy_url_label_unmap(GtkWidget *widget) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->event_window != NULL) gdk_window_hide(priv->event_window); GTK_WIDGET_CLASS(parent_class)->unmap(widget); } static void sexy_url_label_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { SexyUrlLabel *url_label = (SexyUrlLabel *)widget; SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); #if 0 { LabelWrapWidth *wrap_width_data; GtkStyle *style; style = GTK_WIDGET(url_label)->style; wrap_width_data = g_object_get_data(G_OBJECT(style), "gtk-label-wrap-width"); if (wrap_width_data != NULL) printf("wrap width = %d\n", wrap_width_data->width / PANGO_SCALE); } #endif update_wrap_width(url_label, allocation->width); GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation); pango_layout_set_width(gtk_label_get_layout(GTK_LABEL(url_label)), allocation->width * PANGO_SCALE); if (GTK_WIDGET_REALIZED(widget)) { gdk_window_move_resize(priv->event_window, allocation->x, allocation->y, allocation->width, allocation->height); } sexy_url_label_rescan_label(url_label); } static void open_link_activate_cb(GtkMenuItem *menu_item, SexyUrlLabel *url_label) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->active_link == NULL) return; g_signal_emit(url_label, signals[URL_ACTIVATED], 0, priv->active_link->url); } static void copy_link_activate_cb(GtkMenuItem *menu_item, SexyUrlLabel *url_label) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); GtkClipboard *clipboard; if (priv->active_link == NULL) return; clipboard = gtk_widget_get_clipboard(GTK_WIDGET(url_label), GDK_SELECTION_PRIMARY); gtk_clipboard_set_text(clipboard, priv->active_link->url, strlen(priv->active_link->url)); clipboard = gtk_widget_get_clipboard(GTK_WIDGET(url_label), GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clipboard, priv->active_link->url, strlen(priv->active_link->url)); } /** * sexy_url_label_new * * Creates a new SexyUrlLabel widget. * * Returns: a new #SexyUrlLabel. */ GtkWidget * sexy_url_label_new(void) { return g_object_new(SEXY_TYPE_URL_LABEL, NULL); } static void sexy_url_label_clear_links(SexyUrlLabel *url_label) { SexyUrlLabelPrivate *priv = SEXY_URL_LABEL_GET_PRIVATE(url_label); if (priv->links == NULL) 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; GList *url_list; sexy_url_label_clear_links(url_label); if (list == NULL) return; iter = pango_attr_list_get_iterator(list); gtk_label_get_layout_offsets(GTK_LABEL(url_label), &priv->layout_x, &priv->layout_y); priv->layout_x -= GTK_WIDGET(url_label)->allocation.x; priv->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->start = start; link->end = end; 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)); pango_attr_iterator_destroy (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; #if GTK_CHECK_VERSION(2, 10, 0) GdkColor *link_color = NULL; #endif 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 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 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 tag " "on line %d char %d", line_number, char_number); return; } #if GTK_CHECK_VERSION(2, 10, 0) gtk_widget_ensure_style(GTK_WIDGET(url_label)); gtk_widget_style_get(GTK_WIDGET(url_label), "link-color", &link_color, NULL); if (link_color != NULL) { g_string_append_printf(priv->temp_markup_result, "", link_color->red, link_color->green, link_color->blue); } else { g_string_append(priv->temp_markup_result, ""); } #else g_string_append(priv->temp_markup_result, ""); #endif 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, ""); } else { g_string_append_printf(priv->temp_markup_result, "", 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); gchar *newtext = g_markup_escape_text(text, text_len); g_string_append_len(priv->temp_markup_result, newtext, strlen (newtext)); g_free (newtext); } 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, "", 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, "", -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, "", -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; } /** * sexy_url_label_set_markup * @url_label: A #SexyUrlLabel. * @markup: a markup string (see Pango markup format) * * Parses @markup which is marked up with the Pango text markup language as well as * HTML-style hyperlinks, setting the label's text and attribute list based * on the parse results. If the @markup is external data, you may need to * escape it with g_markup_escape_text() or g_markup_printf_escaped() */ void sexy_url_label_set_markup(SexyUrlLabel *url_label, const gchar *markup) { SexyUrlLabelPrivate *priv; gchar *new_markup; g_return_if_fail(SEXY_IS_URL_LABEL(url_label)); priv = SEXY_URL_LABEL_GET_PRIVATE(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); g_free(new_markup); } else { gtk_label_set_markup(GTK_LABEL(url_label), ""); } sexy_url_label_rescan_label(url_label); update_wrap_width(url_label, priv->wrap_width); } // vim:ts=4 sw=4