# HG changeset patch # User Sadrul Habib Chowdhury # Date 1227463761 0 # Node ID 82deaa289d2a6b412c8da42c40d043e990c3cee7 # Parent de3b12b70b5f514d0db5d3f6ea238442c96eed04# Parent 16811be2253f70c6acdc3b61e2e62ef324d6632c propagate from branch 'im.pidgin.pidgin' (head a9579fef71ff6820f390a986132d30b079f590a0) to branch 'im.pidgin.imhtml.customlinks' (head b42b5daebfa64a4919ddf75ed6a9ce256d795d46) diff -r de3b12b70b5f -r 82deaa289d2a ChangeLog.API --- a/ChangeLog.API Mon Nov 17 13:28:32 2008 +0000 +++ b/ChangeLog.API Sun Nov 23 18:09:21 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 de3b12b70b5f -r 82deaa289d2a pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Mon Nov 17 13:28:32 2008 +0000 +++ b/pidgin/gtkimhtml.c Sun Nov 23 18:09:21 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,13 +1818,27 @@ 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 */ img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU); item = gtk_image_menu_item_new_with_mnemonic( - _("_Open Link in Browser")); + _("_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); @@ -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 de3b12b70b5f -r 82deaa289d2a pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Mon Nov 17 13:28:32 2008 +0000 +++ b/pidgin/gtkimhtml.h Sun Nov 23 18:09:21 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 de3b12b70b5f -r 82deaa289d2a pidgin/gtkmain.c --- a/pidgin/gtkmain.c Mon Nov 17 13:28:32 2008 +0000 +++ b/pidgin/gtkmain.c Sun Nov 23 18:09:21 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 de3b12b70b5f -r 82deaa289d2a pidgin/gtkutils.c --- a/pidgin/gtkutils.c Mon Nov 17 13:28:32 2008 +0000 +++ b/pidgin/gtkutils.c Sun Nov 23 18:09:21 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 de3b12b70b5f -r 82deaa289d2a pidgin/gtkutils.h --- a/pidgin/gtkutils.h Mon Nov 17 13:28:32 2008 +0000 +++ b/pidgin/gtkutils.h Sun Nov 23 18:09:21 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_ */