# HG changeset patch # User Sadrul Habib Chowdhury # Date 1226829528 0 # Node ID 38a2f78f80a7963bff68d6379c801cdb0681fa3f # Parent fae699fece1f0072d6a416615f423681c5dd14e1 Allow plugins to specify custom link types to the GtkIMHtml widget. Currently, the custom link types are added for all GtkIMHtml widgets. If we wanted, it should be possible to add custom links to particular widgets only too. If everything looks OK, I might merge this in before 2.6.0 diff -r fae699fece1f -r 38a2f78f80a7 ChangeLog.API --- a/ChangeLog.API Sun Nov 16 00:10:02 2008 +0000 +++ b/ChangeLog.API Sun Nov 16 09:58:48 2008 +0000 @@ -1,5 +1,11 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.6.0 (??/??/????): + pidgin: + Added: + * gtk_imhtml_class_register_protocol + * pidgin_utils_init, pidgin_utils_uninit + version 2.5.0 (08/18/2008): libpurple: Added: diff -r fae699fece1f -r 38a2f78f80a7 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Sun Nov 16 00:10:02 2008 +0000 +++ b/pidgin/gtkimhtml.c Sun Nov 16 09:58:48 2008 +0000 @@ -88,6 +88,15 @@ GtkTextMark *mark; }; +typedef struct _GtkIMHtmlProtocol +{ + char *name; + int length; + + gboolean (*activate)(GtkIMHtml *imhtml, const char *text); + gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu); +} GtkIMHtmlProtocol; + static gboolean gtk_text_view_drag_motion (GtkWidget *widget, GdkDragContext *context, @@ -115,6 +124,7 @@ static void imhtml_font_grow(GtkIMHtml *imhtml); static void imhtml_font_shrink(GtkIMHtml *imhtml); static void imhtml_clear_formatting(GtkIMHtml *imhtml); +static int gtk_imhtml_is_protocol(const char *text); /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ #define MAX_FONT_SIZE 7 @@ -1391,6 +1401,32 @@ } +static GtkIMHtmlProtocol * +imhtml_find_protocol(const char *url) +{ + GtkIMHtmlClass *klass; + GList *iter; + GtkIMHtmlProtocol *proto = NULL; + + klass = g_type_class_ref(GTK_TYPE_IMHTML); + for (iter = klass->protocols; iter; iter = iter->next) { + proto = iter->data; + if (g_ascii_strncasecmp(url, proto->name, proto->length) == 0) { + return proto; + } + } + return NULL; +} + +static void +imhtml_url_clicked(GtkIMHtml *imhtml, const char *url) +{ + GtkIMHtmlProtocol *proto = imhtml_find_protocol(url); + if (!proto) + return; + proto->activate(imhtml, url); /* XXX: Do something with the return value? */ +} + /* Boring GTK+ stuff */ static void gtk_imhtml_class_init (GtkIMHtmlClass *klass) { @@ -1475,6 +1511,7 @@ klass->toggle_format = imhtml_toggle_format; klass->message_send = imhtml_message_send; klass->clear_format = imhtml_clear_formatting; + klass->url_clicked = imhtml_url_clicked; klass->undo = gtk_imhtml_undo; klass->redo = gtk_imhtml_redo; @@ -1745,6 +1782,7 @@ return FALSE; } else if(event_button->button == 3) { GtkWidget *img, *item, *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")); @@ -1766,7 +1804,7 @@ menu = gtk_menu_new(); g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy); - /* buttons and such */ + proto = imhtml_find_protocol(tempdata->url); if (!strncmp(tempdata->url, "mailto:", 7)) { @@ -1780,6 +1818,20 @@ G_CALLBACK(url_copy), tempdata->url + 7); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); } + 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 */ @@ -1884,10 +1936,7 @@ links = g_strsplit((char *)sd->data, "\n", 0); while((link = links[i]) != NULL){ - if(purple_str_has_prefix(link, "http://") || - purple_str_has_prefix(link, "https://") || - purple_str_has_prefix(link, "ftp://")) - { + if (gtk_imhtml_is_protocol(link)) { gchar *label; if(links[i + 1]) @@ -1896,7 +1945,7 @@ label = links[i]; gtk_imhtml_insert_link(imhtml, mark, link, label); - } else if (link=='\0') { + } else if (*link == '\0') { /* Ignore blank lines */ } else { /* Special reasons, aka images being put in via other tag, etc. */ @@ -2382,26 +2431,12 @@ return g_string_free(ret, FALSE); } -static const char *accepted_protocols[] = { - "http://", - "https://", - "ftp://" -}; - -static const int accepted_protocols_size = 3; - /* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so the caller knows how long the protocol string is. */ static int gtk_imhtml_is_protocol(const char *text) { - gint i; - - for(i=0; ilength : 0; } /* @@ -3320,6 +3355,11 @@ pos++; } else if ((len_protocol = gtk_imhtml_is_protocol(c)) > 0){ br = FALSE; + if (wpos > 0) { + gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); + ws[0] = '\0'; + wpos = 0; + } while(len_protocol--){ /* Skip the next len_protocol characters, but make sure they're copied into the ws array. @@ -3327,6 +3367,17 @@ ws [wpos++] = *c++; pos++; } + if (!imhtml->edit.link) { + while (*c && *c != ' ') { + ws [wpos++] = *c++; + pos++; + } + ws[wpos] = '\0'; + gtk_imhtml_toggle_link(imhtml, ws); + gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + gtk_imhtml_toggle_link(imhtml, NULL); + } } else if (*c) { br = FALSE; ws [wpos++] = *c++; @@ -5745,3 +5796,35 @@ g_free(smiley); } +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)) +{ + GtkIMHtmlClass *klass; + GtkIMHtmlProtocol *proto; + + g_return_val_if_fail(name, FALSE); + + klass = g_type_class_ref(GTK_TYPE_IMHTML); + g_return_val_if_fail(klass, FALSE); + + if ((proto = imhtml_find_protocol(name))) { + g_return_val_if_fail(!activate, FALSE); + g_free(proto->name); + g_free(proto); + klass->protocols = g_list_remove(klass->protocols, proto); + return TRUE; + } else { + g_return_val_if_fail(activate, FALSE); + } + + proto = g_new0(GtkIMHtmlProtocol, 1); + proto->name = g_strdup(name); + proto->length = strlen(name); + proto->activate = activate; + proto->context_menu = context_menu; + klass->protocols = g_list_prepend(klass->protocols, proto); + + return TRUE; +} + diff -r fae699fece1f -r 38a2f78f80a7 pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Sun Nov 16 00:10:02 2008 +0000 +++ b/pidgin/gtkimhtml.h Sun Nov 16 09:58:48 2008 +0000 @@ -156,6 +156,7 @@ gboolean (*message_send)(GtkIMHtml *); void (*undo)(GtkIMHtml *); void (*redo)(GtkIMHtml *); + GList *protocols; /* List of GtkIMHtmlProtocol's */ }; struct _GtkIMHtmlFontDetail { @@ -885,6 +886,23 @@ * @since 2.5.0 */ void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley); + +/** + * 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. + * + * @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)); /*@}*/ #ifdef __cplusplus diff -r fae699fece1f -r 38a2f78f80a7 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sun Nov 16 00:10:02 2008 +0000 +++ b/pidgin/gtkmain.c Sun Nov 16 09:58:48 2008 +0000 @@ -310,6 +310,7 @@ pidgin_log_init(); pidgin_docklet_init(); pidgin_smileys_init(); + pidgin_utils_init(); } static GHashTable *ui_info = NULL; @@ -326,6 +327,7 @@ pidgin_plugins_save(); /* Uninit */ + pidgin_utils_uninit(); pidgin_smileys_uninit(); pidgin_conversations_uninit(); pidgin_status_uninit(); diff -r fae699fece1f -r 38a2f78f80a7 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Sun Nov 16 00:10:02 2008 +0000 +++ b/pidgin/gtkutils.c Sun Nov 16 09:58:48 2008 +0000 @@ -56,6 +56,9 @@ #include "signals.h" #include "util.h" +#include "gtkaccount.h" +#include "gtkprefs.h" + #include "gtkconv.h" #include "gtkdialogs.h" #include "gtkimhtml.h" @@ -80,10 +83,11 @@ return FALSE; } -static void -url_clicked_cb(GtkWidget *w, const char *uri) +static gboolean +url_clicked_cb(GtkIMHtml *imhtml, const char *uri) { g_idle_add(url_clicked_idle_cb, g_strdup(uri)); + return TRUE; } static GtkIMHtmlFuncs gtkimhtml_cbs = { @@ -102,9 +106,6 @@ g_return_if_fail(imhtml != NULL); g_return_if_fail(GTK_IS_IMHTML(imhtml)); - g_signal_connect(G_OBJECT(imhtml), "url_clicked", - G_CALLBACK(url_clicked_cb), NULL); - pidgin_themes_smiley_themeize(imhtml); gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), >kimhtml_cbs); @@ -3480,3 +3481,47 @@ return pixbuf; } +/* XXX: The following two functions are for demonstration purposes only! */ +static gboolean +open_dialog(GtkIMHtml *imhtml, const char *url) +{ + const char *str; + + if (strlen(url) < sizeof("open://")) + return FALSE; + + str = url + sizeof("open://") - 1; + + if (strcmp(str, "accounts") == 0) + pidgin_accounts_window_show(); + else if (strcmp(str, "prefs") == 0) + pidgin_prefs_show(); + else + return FALSE; + return TRUE; +} + +static gboolean +dummy(GtkIMHtml *imhtml, const char *text, 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("open://", open_dialog, dummy); +} + +void pidgin_utils_uninit(void) +{ + 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("open://", NULL, NULL); +} + diff -r fae699fece1f -r 38a2f78f80a7 pidgin/gtkutils.h --- a/pidgin/gtkutils.h Sun Nov 16 00:10:02 2008 +0000 +++ b/pidgin/gtkutils.h Sun Nov 16 09:58:48 2008 +0000 @@ -822,5 +822,17 @@ */ GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image); +/** + * Initialize some utility functions. + * @since 2.6.0 + */ +void pidgin_utils_init(void); + +/** + * Uninitialize some utility functions. + * @since 2.6.0 + */ +void pidgin_utils_uninit(void); + #endif /* _PIDGINUTILS_H_ */