# HG changeset patch # User Sadrul Habib Chowdhury # Date 1227465031 0 # Node ID 284fd17c60205a6078e99ba2f08d57a501d48b36 # Parent 82deaa289d2a6b412c8da42c40d043e990c3cee7 Clean up some crufts. I think this is now mergeable with .next.minor. diff -r 82deaa289d2a -r 284fd17c6020 ChangeLog.API --- a/ChangeLog.API Sun Nov 23 18:09:21 2008 +0000 +++ b/ChangeLog.API Sun Nov 23 18:30:31 2008 +0000 @@ -4,6 +4,9 @@ pidgin: Added: * gtk_imhtml_class_register_protocol + * gtk_imhtml_link_get_url, gtk_imhtml_link_get_text_tag, + gtk_imhtml_link_activate functions to process GtkIMHtmlLink objects + from GtkIMHtml protocol callbacks. * pidgin_utils_init, pidgin_utils_uninit version 2.5.0 (08/18/2008): diff -r 82deaa289d2a -r 284fd17c6020 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Sun Nov 23 18:09:21 2008 +0000 +++ b/pidgin/gtkimhtml.c Sun Nov 23 18:30:31 2008 +0000 @@ -88,13 +88,20 @@ GtkTextMark *mark; }; +struct _GtkIMHtmlLink +{ + GtkIMHtml *imhtml; + gchar *url; + GtkTextTag *tag; +}; + typedef struct _GtkIMHtmlProtocol { char *name; int length; - gboolean (*activate)(GtkIMHtml *imhtml, const char *text); - gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu); + gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link); + gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu); } GtkIMHtmlProtocol; static gboolean @@ -125,6 +132,8 @@ static void imhtml_font_shrink(GtkIMHtml *imhtml); static void imhtml_clear_formatting(GtkIMHtml *imhtml); static int gtk_imhtml_is_protocol(const char *text); +static void gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag); +static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link); /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ #define MAX_FONT_SIZE 7 @@ -1422,9 +1431,14 @@ imhtml_url_clicked(GtkIMHtml *imhtml, const char *url) { GtkIMHtmlProtocol *proto = imhtml_find_protocol(url); + GtkIMHtmlLink *link; if (!proto) return; - proto->activate(imhtml, url); /* XXX: Do something with the return value? */ + link = g_new0(GtkIMHtmlLink, 1); + link->imhtml = g_object_ref(imhtml); + link->url = g_strdup(url); + proto->activate(imhtml, link); /* XXX: Do something with the return value? */ + gtk_imhtml_link_destroy(link); } /* Boring GTK+ stuff */ @@ -1725,37 +1739,14 @@ return imhtml_type; } -struct url_data { - GObject *object; - gchar *url; - GtkTextTag *tag; -}; - -static void url_data_destroy(gpointer mydata) -{ - struct url_data *data = mydata; - g_object_unref(data->object); - g_object_unref(data->tag); - g_free(data->url); - g_free(data); -} - -static void url_open(GtkWidget *w, struct url_data *data) -{ - if(!data) return; - g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url); - g_object_set_data(G_OBJECT(data->tag), "visited", GINT_TO_POINTER(TRUE)); - gtk_imhtml_set_link_color(GTK_IMHTML(data->object), data->tag); -} - -static void url_copy(GtkWidget *w, gchar *url) { - GtkClipboard *clipboard; - - clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY); - gtk_clipboard_set_text(clipboard, url, -1); - - clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD); - gtk_clipboard_set_text(clipboard, url, -1); +static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link) +{ + if (link->imhtml) + g_object_unref(link->imhtml); + if (link->tag) + g_object_unref(link->tag); + g_free(link->url); + g_free(link); } /* The callback for an event on a link tag. */ @@ -1771,22 +1762,16 @@ if (gtk_text_buffer_get_selection_bounds( gtk_text_iter_get_buffer(arg2), &start, &end)) return FALSE; - - /* A link was clicked--we emit the "url_clicked" signal - * with the URL as the argument */ - g_object_ref(G_OBJECT(tag)); - g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url")); - g_object_unref(G_OBJECT(tag)); - g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); - gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); + gtk_imhtml_activate_tag(GTK_IMHTML(imhtml), tag); return FALSE; } else if(event_button->button == 3) { - GtkWidget *img, *item, *menu; + GList *children; + GtkWidget *menu; GtkIMHtmlProtocol *proto; - struct url_data *tempdata = g_new(struct url_data, 1); - tempdata->object = g_object_ref(imhtml); - tempdata->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); - tempdata->tag = g_object_ref(tag); + GtkIMHtmlLink *link = g_new(GtkIMHtmlLink, 1); + link->imhtml = g_object_ref(imhtml); + link->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); + link->tag = g_object_ref(tag); /* Don't want the tooltip around if user right-clicked on link */ if (GTK_IMHTML(imhtml)->tip_window) { @@ -1802,57 +1787,23 @@ else gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); menu = gtk_menu_new(); - g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy); - - proto = imhtml_find_protocol(tempdata->url); - - if (!strncmp(tempdata->url, "mailto:", 7)) - { - /* Copy Email Address */ - img = gtk_image_new_from_stock(GTK_STOCK_COPY, - GTK_ICON_SIZE_MENU); - item = gtk_image_menu_item_new_with_mnemonic( - _("_Copy Email Address")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); - g_signal_connect(G_OBJECT(item), "activate", - G_CALLBACK(url_copy), tempdata->url + 7); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", link, + (GDestroyNotify)gtk_imhtml_link_destroy); + + proto = imhtml_find_protocol(link->url); + + if (proto && proto->context_menu) { + proto->context_menu(GTK_IMHTML(link->imhtml), link, menu); } - else if (proto && proto->context_menu) - { - GList *children; - proto->context_menu(GTK_IMHTML(tempdata->object), tempdata->url, menu); - children = gtk_container_get_children(GTK_CONTAINER(menu)); - if (!children) { - item = gtk_menu_item_new_with_label(_("No actions available")); - gtk_widget_show(item); - gtk_widget_set_sensitive(item, FALSE); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - } else { - g_list_free(children); - } - } - else - { - /* Open Link in Browser */ - img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, - GTK_ICON_SIZE_MENU); - item = gtk_image_menu_item_new_with_mnemonic( - _("_Open Link")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); - g_signal_connect(G_OBJECT(item), "activate", - G_CALLBACK(url_open), tempdata); + + children = gtk_container_get_children(GTK_CONTAINER(menu)); + if (!children) { + GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available")); + gtk_widget_show(item); + gtk_widget_set_sensitive(item, FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - /* Copy Link Location */ - img = gtk_image_new_from_stock(GTK_STOCK_COPY, - GTK_ICON_SIZE_MENU); - item = gtk_image_menu_item_new_with_mnemonic( - _("_Copy Link Location")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); - g_signal_connect(G_OBJECT(item), "activate", - G_CALLBACK(url_copy), tempdata->url); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } else { + g_list_free(children); } @@ -5797,8 +5748,8 @@ } gboolean gtk_imhtml_class_register_protocol(const char *name, - gboolean (*activate)(GtkIMHtml *imhtml, const char *text), - gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu)) + gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link), + gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)) { GtkIMHtmlClass *klass; GtkIMHtmlProtocol *proto; @@ -5828,3 +5779,38 @@ return TRUE; } +static void +gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag) +{ + /* A link was clicked--we emit the "url_clicked" signal + * with the URL as the argument */ + g_object_ref(G_OBJECT(tag)); + g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url")); + g_object_unref(G_OBJECT(tag)); + g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); + gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); +} + +gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link) +{ + g_return_val_if_fail(link, FALSE); + + if (link->tag) { + gtk_imhtml_activate_tag(link->imhtml, link->tag); + } else if (link->url) { + g_signal_emit(link->imhtml, signals[URL_CLICKED], 0, link->url); + } else + return FALSE; + return TRUE; +} + +const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link) +{ + return link->url; +} + +const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link) +{ + return link->tag; +} + diff -r 82deaa289d2a -r 284fd17c6020 pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Sun Nov 23 18:09:21 2008 +0000 +++ b/pidgin/gtkimhtml.h Sun Nov 23 18:30:31 2008 +0000 @@ -60,6 +60,7 @@ typedef struct _GtkIMHtmlAnimation GtkIMHtmlAnimation; typedef struct _GtkIMHtmlHr GtkIMHtmlHr; typedef struct _GtkIMHtmlFuncs GtkIMHtmlFuncs; +typedef struct _GtkIMHtmlLink GtkIMHtmlLink; typedef enum { GTK_IMHTML_BOLD = 1 << 0, @@ -888,21 +889,57 @@ void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley); /** - * Register a protocol with the GtkIMHtml widget. Registering a protocol would allow - * certain text to be clickable. + * Register a protocol with the GtkIMHtml widget. Registering a protocol would + * allow certain text to be clickable. * * @param name The name of the protocol (e.g. http://) * @param activate The callback to trigger when the protocol text is clicked. - * Removes any current protocol definition if @c NULL. - * @param context_menu The callback totrigger when the context menu is popped up on - * the protocol text. + * Removes any current protocol definition if @c NULL. The + * callback should return @c TRUE if the link was activated + * properly, @c FALSE otherwise. + * @param context_menu The callback to trigger when the context menu is popped + * up on the protocol text. The callback should return + * @c TRUE if the request for context menu was processed + * successfully, @c FALSE otherwise. * * @return @c TRUE if the protocol was successfully registered (or unregistered, when #activate is @c NULL) * @since 2.6.0 */ gboolean gtk_imhtml_class_register_protocol(const char *name, - gboolean (*activate)(GtkIMHtml *imhtml, const char *text), - gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu)); + gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link), + gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)); + +/** + * Get the URL associated with a link. This should be used by the IMHtml protocol-callbacks. + * + * @param link The GtkIMHtmlLink object sent to the callback functions + * + * @return The URL + * @since 2.6.0 + */ +const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link); + +/** + * Get the GtkTextTag object (if any) associated with a particular link. + * + * @param link The GtkIMHtmlLink object sent to the callback functions + * + * @return The GtkTextTag object, or @c NULL + * @since 2.6.0 + */ +const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link); + +/** + * Activates a GtkIMHtmlLink object. This triggers the 'url-clicked' signal, marks the + * link as visited (when possible). + * + * @param link The GtkIMHtmlLink object sent to the callback functions + * + * @return @c TRUE if 'url-clicked' signal was emitted, @c FALSE otherwise. + * @since 2.6.0 + */ +gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link); + /*@}*/ #ifdef __cplusplus diff -r 82deaa289d2a -r 284fd17c6020 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Sun Nov 23 18:09:21 2008 +0000 +++ b/pidgin/gtkutils.c Sun Nov 23 18:30:31 2008 +0000 @@ -84,8 +84,9 @@ } static gboolean -url_clicked_cb(GtkIMHtml *imhtml, const char *uri) +url_clicked_cb(GtkIMHtml *unused, GtkIMHtmlLink *link) { + const char *uri = gtk_imhtml_link_get_url(link); g_idle_add(url_clicked_idle_cb, g_strdup(uri)); return TRUE; } @@ -3481,13 +3482,73 @@ return pixbuf; } +static void url_copy(GtkWidget *w, gchar *url) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text(clipboard, url, -1); + + clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text(clipboard, url, -1); +} + +static gboolean +link_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu) +{ + GtkWidget *img, *item; + const char *url; + + url = gtk_imhtml_link_get_url(link); + + /* Open Link in Browser */ + img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU); + item = gtk_image_menu_item_new_with_mnemonic(_("_Open Link")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); + g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_link_activate), link); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + /* Copy Link Location */ + img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU); + item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Link Location")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), (gpointer)url); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + return TRUE; +} + +static gboolean +copy_email_address(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu) +{ + GtkWidget *img, *item; + const char *text; + char *address; +#define MAILTOSIZE (sizeof("mailto:") - 1) + + text = gtk_imhtml_link_get_url(link); + g_return_val_if_fail(text && strlen(text) > MAILTOSIZE, FALSE); + address = (char*)text + MAILTOSIZE; + + /* Copy Email Address */ + img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU); + item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Email Address")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), address); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + return TRUE; +} + /* XXX: The following two functions are for demonstration purposes only! */ static gboolean -open_dialog(GtkIMHtml *imhtml, const char *url) +open_dialog(GtkIMHtml *imhtml, GtkIMHtmlLink *link) { + const char *url; const char *str; - if (strlen(url) < sizeof("open://")) + url = gtk_imhtml_link_get_url(link); + if (!url || strlen(url) < sizeof("open://")) return FALSE; str = url + sizeof("open://") - 1; @@ -3502,16 +3563,18 @@ } static gboolean -dummy(GtkIMHtml *imhtml, const char *text, GtkWidget *menu) +dummy(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu) { return TRUE; } void pidgin_utils_init(void) { - gtk_imhtml_class_register_protocol("http://", url_clicked_cb, NULL); - gtk_imhtml_class_register_protocol("https://", url_clicked_cb, NULL); - gtk_imhtml_class_register_protocol("ftp://", url_clicked_cb, NULL); + gtk_imhtml_class_register_protocol("http://", url_clicked_cb, link_context_menu); + gtk_imhtml_class_register_protocol("https://", url_clicked_cb, link_context_menu); + gtk_imhtml_class_register_protocol("ftp://", url_clicked_cb, link_context_menu); + gtk_imhtml_class_register_protocol("gopher://", url_clicked_cb, link_context_menu); + gtk_imhtml_class_register_protocol("mailto:", url_clicked_cb, copy_email_address); gtk_imhtml_class_register_protocol("open://", open_dialog, dummy); } @@ -3521,6 +3584,8 @@ gtk_imhtml_class_register_protocol("http://", NULL, NULL); gtk_imhtml_class_register_protocol("https://", NULL, NULL); gtk_imhtml_class_register_protocol("ftp://", NULL, NULL); + gtk_imhtml_class_register_protocol("mailto:", NULL, NULL); + gtk_imhtml_class_register_protocol("gopher://", NULL, NULL); gtk_imhtml_class_register_protocol("open://", NULL, NULL); }