--- a/pidgin/gtkwebview.c Mon Aug 13 03:43:42 2012 -0400
+++ b/pidgin/gtkwebview.c Mon Aug 13 18:25:19 2012 -0400
@@ -75,6 +75,26 @@
gboolean (*context_menu)(GtkWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu);
+struct _GtkWebViewSmiley { + GdkPixbufAnimation *icon; + GdkPixbufLoader *loader; + GtkWebViewSmileyFlags flags; +typedef struct _GtkSmileyTree GtkSmileyTree; + GtkSmileyTree **children; + GtkWebViewSmiley *image; typedef struct _GtkWebViewPriv {
@@ -94,6 +114,10 @@
gboolean block_changed:1;
+ GHashTable *smiley_data; + GtkSmileyTree *default_smilies; /******************************************************************************
@@ -103,6 +127,484 @@
static WebKitWebViewClass *parent_class = NULL;
/******************************************************************************
+ *****************************************************************************/ +gtk_webview_get_protocol_name(GtkWebView *webview) + g_return_val_if_fail(webview != NULL, NULL); + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + return priv->protocol_name; +gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name) + g_return_if_fail(webview != NULL); + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + priv->protocol_name = g_strdup(protocol_name); +gtk_smiley_tree_new(void) + return g_new0(GtkSmileyTree, 1); +gtk_smiley_tree_insert(GtkSmileyTree *tree, GtkWebViewSmiley *smiley) + GtkSmileyTree *t = tree; + const char *x = smiley->smile; + t->values = g_string_new(""); + pos = strchr(t->values->str, *x); + t->values = g_string_append_c(t->values, *x); + index = t->values->len - 1; + t->children = g_realloc(t->children, t->values->len * sizeof(GtkSmileyTree *)); + t->children[index] = g_new0(GtkSmileyTree, 1); + index = pos - t->values->str; + t = t->children[index]; +gtk_smiley_tree_destroy(GtkSmileyTree *tree) + GSList *list = g_slist_prepend(NULL, tree); + GtkSmileyTree *t = list->data; + list = g_slist_delete_link(list, list); + for (i = 0; i < t->values->len; i++) + list = g_slist_prepend(list, t->children[i]); + g_string_free(t->values, TRUE); +gtk_smiley_tree_remove(GtkSmileyTree *tree, GtkWebViewSmiley *smiley) + GtkSmileyTree *t = tree; + const gchar *x = smiley->smile; + pos = strchr(t->values->str, *x); + t = t->children[pos - t->values->str]; +gtk_smiley_tree_lookup(GtkSmileyTree *tree, const char *text) + GtkSmileyTree *t = tree; + if (*x == '&' && (amp = purple_markup_unescape_entity(x, &alen))) { + gboolean matched = TRUE; + /* Make sure all chars of the unescaped value match */ + pos = strchr(t->values->str, *amp); + t = t->children[pos - t->values->str]; + pos = strchr(t->values->str, *amp); + else if (*x == '<') /* Because we're all WYSIWYG now, a '<' char should + * only appear as the start of a tag. Perhaps a + * safer (but costlier) check would be to call + * gtk_imhtml_is_tag on it */ + pos = strchr(t->values->str, *x); + t = t->children[pos - t->values->str]; +gtk_webview_disassociate_smiley_foreach(gpointer key, gpointer value, + GtkSmileyTree *tree = (GtkSmileyTree *)value; + GtkWebViewSmiley *smiley = (GtkWebViewSmiley *)user_data; + gtk_smiley_tree_remove(tree, smiley); +gtk_webview_disconnect_smiley(GtkWebView *webview, GtkWebViewSmiley *smiley) + smiley->webview = NULL; + g_signal_handlers_disconnect_matched(webview, G_SIGNAL_MATCH_DATA, 0, 0, +gtk_webview_disassociate_smiley(GtkWebViewSmiley *smiley) + GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(smiley->webview); + gtk_smiley_tree_remove(priv->default_smilies, smiley); + g_hash_table_foreach(priv->smiley_data, + gtk_webview_disassociate_smiley_foreach, smiley); + g_signal_handlers_disconnect_matched(smiley->webview, + G_SIGNAL_MATCH_DATA, 0, 0, NULL, + smiley->webview = NULL; +gtk_webview_associate_smiley(GtkWebView *webview, const char *sml, + GtkWebViewSmiley *smiley) + g_return_if_fail(webview != NULL); + g_return_if_fail(GTK_IS_WEBVIEW(webview)); + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + tree = priv->default_smilies; + else if (!(tree = g_hash_table_lookup(priv->smiley_data, sml))) { + tree = gtk_smiley_tree_new(); + g_hash_table_insert(priv->smiley_data, g_strdup(sml), tree); + /* need to disconnect old webview, if there is one */ + g_signal_handlers_disconnect_matched(smiley->webview, + G_SIGNAL_MATCH_DATA, 0, 0, NULL, + smiley->webview = webview; + gtk_smiley_tree_insert(tree, smiley); + /* connect destroy signal for the webview */ + g_signal_connect(webview, "destroy", + G_CALLBACK(gtk_webview_disconnect_smiley), smiley); +gtk_webview_is_smiley(GtkWebViewPriv *priv, const char *sml, const char *text, + sml = priv->protocol_name; + if (!sml || !(tree = g_hash_table_lookup(priv->smiley_data, sml))) + tree = priv->default_smilies; + *len = gtk_smiley_tree_lookup(tree, text); +static GtkWebViewSmiley * +gtk_webview_smiley_get_from_tree(GtkSmileyTree *t, const char *text) + pos = strchr(t->values->str, *x); + t = t->children[pos - t->values->str]; +gtk_webview_smiley_find(GtkWebView *webview, const char *sml, const char *text) + g_return_val_if_fail(webview != NULL, NULL); + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + /* Look for custom smileys first */ + ret = gtk_webview_smiley_get_from_tree(g_hash_table_lookup(priv->smiley_data, sml), text); + /* Fall back to check for default smileys */ + return gtk_webview_smiley_get_from_tree(priv->default_smilies, text); +static GdkPixbufAnimation * +gtk_smiley_get_image(GtkWebViewSmiley *smiley) + smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL); + } else if (smiley->loader) { + smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader); + g_object_ref(G_OBJECT(smiley->icon)); +gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data) + GtkWebViewSmiley *smiley; + smiley = (GtkWebViewSmiley *)user_data; + smiley->icon = gdk_pixbuf_loader_get_animation(loader); + g_object_ref(G_OBJECT(smiley->icon)); +gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data) + GtkWebViewSmiley *smiley; + GtkWidget *icon = NULL; + GtkTextChildAnchor *anchor = NULL; + GSList *current = NULL; + smiley = (GtkWebViewSmiley *)user_data; + if (!smiley->webview) { + g_object_unref(G_OBJECT(loader)); + for (current = smiley->anchors; current; current = g_slist_next(current)) { + anchor = GTK_TEXT_CHILD_ANCHOR(current->data); + if (gtk_text_child_anchor_get_deleted(anchor)) + icon = gtk_image_new_from_animation(smiley->icon); + wids = gtk_text_child_anchor_get_widgets(anchor); + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", + purple_unescape_html(smiley->smile), g_free); + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", + g_strdup(smiley->smile), g_free); + GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data)); + g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL); + gtk_container_add(GTK_CONTAINER(wids->data), icon); + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->webview), icon, anchor); + g_object_unref(anchor); + g_slist_free(smiley->anchors); + smiley->anchors = NULL; + g_object_unref(G_OBJECT(loader)); +gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data) + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) { + int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size"); + if (width <= custom_smileys_size && height <= custom_smileys_size) + height = height * custom_smileys_size / width; + width = custom_smileys_size; + width = width * custom_smileys_size / height; + height = custom_smileys_size; + gdk_pixbuf_loader_set_size(loader, width, height); +gtk_webview_smiley_create(const char *file, const char *shortcut, gboolean hide, + GtkWebViewSmileyFlags flags) + GtkWebViewSmiley *smiley = g_new0(GtkWebViewSmiley, 1); + smiley->file = g_strdup(file); + smiley->smile = g_strdup(shortcut); + smiley->webview = NULL; + gtk_webview_smiley_reload(smiley); +gtk_webview_smiley_reload(GtkWebViewSmiley *smiley) + g_object_unref(smiley->icon); + g_object_unref(smiley->loader); + /* We do not use the pixbuf loader for a smiley that can be loaded + * from a file. (e.g., local custom smileys) + smiley->loader = gdk_pixbuf_loader_new(); + g_signal_connect(smiley->loader, "area_prepared", + G_CALLBACK(gtk_custom_smiley_allocated), smiley); + g_signal_connect(smiley->loader, "closed", + G_CALLBACK(gtk_custom_smiley_closed), smiley); + g_signal_connect(smiley->loader, "size_prepared", + G_CALLBACK(gtk_custom_smiley_size_prepared), smiley); +gtk_webview_smiley_get_flags(GtkWebViewSmiley *smiley) +gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley) + gtk_webview_disassociate_smiley(smiley); + g_object_unref(smiley->icon); + g_object_unref(smiley->loader); +gtk_webview_remove_smileys(GtkWebView *webview) + g_return_if_fail(webview != NULL); + priv = GTK_WEBVIEW_GET_PRIVATE(webview); + g_hash_table_destroy(priv->smiley_data); + gtk_smiley_tree_destroy(priv->default_smilies); + priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gtk_smiley_tree_destroy); + priv->default_smilies = gtk_smiley_tree_new(); +/****************************************************************************** *****************************************************************************/
@@ -670,6 +1172,10 @@
g_queue_free(priv->load_queue);
+ g_hash_table_destroy(priv->smiley_data); + gtk_smiley_tree_destroy(priv->default_smilies); + g_free(priv->protocol_name); G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
@@ -760,6 +1266,10 @@
priv->load_queue = g_queue_new();
+ priv->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gtk_smiley_tree_destroy); + priv->default_smilies = gtk_smiley_tree_new(); g_signal_connect(G_OBJECT(webview), "button-press-event",
G_CALLBACK(webview_button_pressed), NULL);
--- a/pidgin/gtkwebview.h Mon Aug 13 03:43:42 2012 -0400
+++ b/pidgin/gtkwebview.h Mon Aug 13 18:25:19 2012 -0400
@@ -58,8 +58,13 @@
+ GTK_WEBVIEW_SMILEY_CUSTOM = 1 << 0 +} GtkWebViewSmileyFlags; typedef struct _GtkWebView GtkWebView;
typedef struct _GtkWebViewClass GtkWebViewClass;
+typedef struct _GtkWebViewSmiley GtkWebViewSmiley; @@ -495,6 +500,95 @@
void gtk_webview_insert_image(GtkWebView *webview, int id);
+ * Gets the protocol name associated with this GtkWebView. + * @param webview The GtkWebView +const char *gtk_webview_get_protocol_name(GtkWebView *webview); + * Associates a protocol name with a GtkWebView. + * @param webview The GtkWebView + * @param protocol_name The protocol name to associate with the GtkWebView +void gtk_webview_set_protocol_name(GtkWebView *webview, const char *protocol_name); + * Create a new GtkWebViewSmiley. + * @param file The image file for the smiley + * @param shortcut The key shortcut for the smiley + * @param hide @c TRUE if the smiley should be hidden in the smiley dialog, + * @param flags The smiley flags + * @return The newly created smiley +GtkWebViewSmiley *gtk_webview_smiley_create(const char *file, + GtkWebViewSmileyFlags flags); + * Reload the image data for the smiley. + * @param smiley The smiley to reload +void gtk_webview_smiley_reload(GtkWebViewSmiley *smiley); + * Destroy a GtkWebViewSmiley. + * @param smiley The smiley to destroy +void gtk_webview_smiley_destroy(GtkWebViewSmiley *smiley); + * Returns the flags associated with a smiley. + * @param smiley The smiley +GtkWebViewSmileyFlags gtk_webview_smiley_get_flags(GtkWebViewSmiley *smiley); + * Returns the smiley object associated with the text. + * @param webview The GtkWebView + * @param sml The name of the smiley category + * @param text The text associated with the smiley +GtkWebViewSmiley *gtk_webview_smiley_find(GtkWebView *webview, const char *sml, + * Associates a smiley with a GtkWebView. + * @param webview The GtkWebView + * @param sml The name of the smiley category + * @param smiley The GtkWebViewSmiley to associate +void gtk_webview_associate_smiley(GtkWebView *webview, const char *sml, + GtkWebViewSmiley *smiley); + * Removes all smileys associated with a GtkWebView. + * @param webview The GtkWebView. +void gtk_webview_remove_smileys(GtkWebView *webview); + * Inserts a smiley at the current location or selection in a GtkWebView. + * @param webview The GtkWebView + * @param sml The category of the smiley + * @param smiley The text of the smiley to insert +void gtk_webview_insert_smiley(GtkWebView *webview, const char *sml, #endif /* _PIDGIN_WEBVIEW_H_ */