changeset 14852:cf25420b074d

[gaim-migrate @ 17621] Allow custom buddy icons for people in the buddylist. It's done completely in the UI side. The custom icon does not overwrite the original icon, which is displayed in the tooltip. You can set the icon by dragging an image on the conv window, or on the buddy in the buddylist. You can also set/remove custom icon from the conversation window by right clicking on the icon in the conv window. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sun, 29 Oct 2006 23:35:57 +0000
parents 036927fddcba
children 910e783fbbc7
files ChangeLog.API gtk/gtkblist.c gtk/gtkconv.c gtk/gtkutils.c gtk/gtkutils.h
diffstat 5 files changed, 182 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Sun Oct 29 23:28:37 2006 +0000
+++ b/ChangeLog.API	Sun Oct 29 23:35:57 2006 +0000
@@ -138,6 +138,7 @@
 	  a change in the values stored in the column.
 	* gaim_find_buddies() returns a list of all buddies in the account if name
 	  is NULL.
+	* gaim_gtk_set_custom_buddy_icon() sets custom icon for a user.
 
 	Removed:
 	* gaim_gtk_sound_{get,set}_mute() (replaced by the /gaim/gtk/sound/mute
--- a/gtk/gtkblist.c	Sun Oct 29 23:28:37 2006 +0000
+++ b/gtk/gtkblist.c	Sun Oct 29 23:35:57 2006 +0000
@@ -1992,12 +1992,12 @@
 }
 
 static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(GaimBlistNode *node,
-		gboolean scaled, gboolean greyed)
+		gboolean scaled, gboolean greyed, gboolean custom)
 {
 	GdkPixbuf *buf, *ret = NULL;
 	GdkPixbufLoader *loader;
 	GaimBuddyIcon *icon;
-	const guchar *data;
+	const guchar *data = NULL;
 	gsize len;
 	GaimBuddy *buddy = (GaimBuddy *)node;
 
@@ -2014,12 +2014,30 @@
 		return NULL;
 #endif
 
-	if (!(icon = gaim_buddy_get_icon(buddy)))
-		if (!(icon = gaim_buddy_icons_find(buddy->account, buddy->name))) /* Not sure I like this...*/
-			return NULL;
+	if (custom) {
+		const char *file = gaim_blist_node_get_string((GaimBlistNode*)gaim_buddy_get_contact(buddy),
+							"custom_buddy_icon");
+		if (file && *file) {
+			char *contents;
+			GError *err  = NULL;
+			if (!g_file_get_contents(file, &contents, &len, &err)) {
+				gaim_debug_info("custom -icon", "Could not open custom-icon %s for %s\n",
+							file, gaim_buddy_get_name(buddy), err->message);
+				g_error_free(err);
+			} else
+				data = (const guchar*)contents;
+		}
+	}
+
+	if (data == NULL) {
+		if (!(icon = gaim_buddy_get_icon(buddy)))
+			if (!(icon = gaim_buddy_icons_find(buddy->account, buddy->name))) /* Not sure I like this...*/
+				return NULL;
+		data = gaim_buddy_icon_get_data(icon, &len);
+		custom = FALSE;  /* We are not using the custom icon */
+	}
 
 	loader = gdk_pixbuf_loader_new();
-	data = gaim_buddy_icon_get_data(icon, &len);
 	gdk_pixbuf_loader_write(loader, data, len, NULL);
 	gdk_pixbuf_loader_close(loader, NULL);
 	buf = gdk_pixbuf_loader_get_pixbuf(loader);
@@ -2027,6 +2045,8 @@
 		g_object_ref(G_OBJECT(buf));
 	g_object_unref(G_OBJECT(loader));
 
+	if (custom)
+		g_free((void*)data);
 	if (buf) {
 		GaimAccount *account = gaim_buddy_get_account(buddy);
 		GaimPluginProtocolInfo *prpl_info = NULL;
@@ -2088,7 +2108,7 @@
 	struct tooltip_data *td = g_new0(struct tooltip_data, 1);
 
 	td->status_icon = gaim_gtk_blist_get_status_icon(node, GAIM_STATUS_ICON_LARGE);
-	td->avatar = gaim_gtk_blist_get_buddy_icon(node, !full, FALSE);
+	td->avatar = gaim_gtk_blist_get_buddy_icon(node, !full, FALSE, FALSE);
 	tooltip_text = gaim_get_tooltip_text(node, full);
 	td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
 	pango_layout_set_markup(td->layout, tooltip_text, -1);
@@ -4470,7 +4490,7 @@
 	status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy,
 						biglist ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL);
 
-	avatar = gaim_gtk_blist_get_buddy_icon((GaimBlistNode *)buddy, TRUE, TRUE);
+	avatar = gaim_gtk_blist_get_buddy_icon((GaimBlistNode *)buddy, TRUE, TRUE, TRUE);
 	mark = gaim_gtk_blist_get_name_markup(buddy, selected);
 
 	if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") &&
--- a/gtk/gtkconv.c	Sun Oct 29 23:28:37 2006 +0000
+++ b/gtk/gtkconv.c	Sun Oct 29 23:35:57 2006 +0000
@@ -2354,7 +2354,7 @@
 }
 
 static void
-remove_icon(GaimGtkConversation *gtkconv)
+remove_icon(GtkWidget *widget, GaimGtkConversation *gtkconv)
 {
 	GaimConversation *conv = gtkconv->active_conv;
 	GaimGtkWindow *gtkwin;
@@ -2412,6 +2412,53 @@
 	fclose(fp);
 }
 
+static const char *
+custom_icon_pref_name(GaimGtkConversation *gtkconv)
+{
+	GaimConversation *conv;
+	GaimAccount *account;
+	GaimBuddy *buddy;
+
+	conv = gtkconv->active_conv;
+	account = gaim_conversation_get_account(conv);
+	buddy = gaim_find_buddy(account, gaim_conversation_get_name(conv));
+	if (buddy) {
+		GaimContact *contact = gaim_buddy_get_contact(buddy);
+		return gaim_blist_node_get_string((GaimBlistNode*)contact, "custom_buddy_icon");
+	}
+	return NULL;
+}
+
+static void
+custom_icon_sel_cb(const char *filename, gpointer data)
+{
+	if (filename) {
+		GaimGtkConversation *gtkconv = data;
+		GaimConversation *conv = gtkconv->active_conv;
+		GaimAccount *account = gaim_conversation_get_account(conv);
+		gaim_gtk_set_custom_buddy_icon(account, gaim_conversation_get_name(conv), filename);
+	}
+}
+
+static void
+set_custom_icon_cb(GtkWidget *widget, GaimGtkConversation *gtkconv)
+{
+	GtkWidget *win = gaim_gtk_buddy_icon_chooser_new(GTK_WINDOW(gtkconv->win->window),
+						custom_icon_sel_cb, gtkconv);
+	gtk_widget_show_all(win);
+}
+
+static void
+remove_custom_icon_cb(GtkWidget *widget, GaimGtkConversation *gtkconv)
+{
+	GaimConversation *conv;
+	GaimAccount *account;
+
+	conv = gtkconv->active_conv;
+	account = gaim_conversation_get_account(conv);
+	gaim_gtk_set_custom_buddy_icon(account, gaim_conversation_get_name(conv), NULL);
+}
+
 static void
 icon_menu_save_cb(GtkWidget *widget, GaimGtkConversation *gtkconv)
 {
@@ -2459,7 +2506,7 @@
 icon_menu(GtkObject *obj, GdkEventButton *e, GaimGtkConversation *gtkconv)
 {
 	static GtkWidget *menu = NULL;
-	GtkWidget *item;
+	const char *pref;
 
 	if (e->button != 3 || e->type != GDK_BUTTON_PRESS)
 		return FALSE;
@@ -2481,16 +2528,25 @@
 							gtkconv->u.im->icon_timer);
 	}
 
-	item = gtk_menu_item_new_with_label(_("Hide Icon"));
-	g_signal_connect_swapped(G_OBJECT(item), "activate",
-							 G_CALLBACK(remove_icon), gtkconv);
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-	gtk_widget_show(item);
+	gaim_new_item_from_stock(menu, _("Hide Icon"), NULL, G_CALLBACK(remove_icon),
+							 gtkconv, 0, 0, NULL);
 
 	gaim_new_item_from_stock(menu, _("Save Icon As..."), GTK_STOCK_SAVE_AS,
 							 G_CALLBACK(icon_menu_save_cb), gtkconv,
 							 0, 0, NULL);
 
+	gaim_new_item_from_stock(menu, _("Set Custom Icon..."), NULL,
+							 G_CALLBACK(set_custom_icon_cb), gtkconv,
+							 0, 0, NULL);
+
+	/* Is there a custom icon for this person? */
+	pref = custom_icon_pref_name(gtkconv);
+	if (pref && *pref) {
+		gaim_new_item_from_stock(menu, _("Remove Custom Icon"), NULL,
+							G_CALLBACK(remove_custom_icon_cb), gtkconv,
+							0, 0, NULL);
+	}
+
 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time);
 
 	return TRUE;
@@ -2518,7 +2574,7 @@
 	if (active)
 		gaim_gtkconv_update_buddy_icon(conv);
 	else
-		remove_icon(gtkconv);
+		remove_icon(NULL, gtkconv);
 }
 
 /**************************************************************************
@@ -5836,7 +5892,8 @@
 	GdkPixbufAnimation *anim;
 	GError *err = NULL;
 
-	const void *data;
+	const char *custom = NULL;
+	const void *data = NULL;
 	size_t len;
 
 	GdkPixbuf *buf;
@@ -5893,12 +5950,28 @@
 	if (gaim_conversation_get_gc(conv) == NULL)
 		return;
 
-	icon = gaim_conv_im_get_icon(GAIM_CONV_IM(conv));
-
-	if (icon == NULL)
-		return;
-
-	data = gaim_buddy_icon_get_data(icon, &len);
+	custom = custom_icon_pref_name(gtkconv);
+	if (custom) {
+		/* There is a custom icon for this user */
+		char *contents = NULL;
+		if (!g_file_get_contents(custom, &contents, &len, &err)) {
+			gaim_debug_warning("custom icon", "could not load custom icon %s for %s\n",
+						custom, gaim_conversation_get_name(conv));
+			g_error_free(err);
+			err = NULL;
+		} else
+			data = contents;
+	}
+
+	if (data == NULL) {
+		icon = gaim_conv_im_get_icon(GAIM_CONV_IM(conv));
+
+		if (icon == NULL)
+			return;
+
+		data = gaim_buddy_icon_get_data(icon, &len);
+		custom = NULL;
+	}
 
 	loader = gdk_pixbuf_loader_new();
 	gdk_pixbuf_loader_write(loader, data, len, NULL);
@@ -5908,6 +5981,9 @@
 		g_object_ref(G_OBJECT(anim));
 	g_object_unref(loader);
 
+	if (custom)
+		g_free((void*)data);
+
 	if (!anim)
 		return;
 	gtkconv->u.im->anim = anim;
--- a/gtk/gtkutils.c	Sun Oct 29 23:28:37 2006 +0000
+++ b/gtk/gtkutils.c	Sun Oct 29 23:35:57 2006 +0000
@@ -1,5 +1,5 @@
 /**
- * @file gtkutils.h GTK+ utility functions
+ * @file gtkutils.c GTK+ utility functions
  * @ingroup gtkui
  *
  * gaim
@@ -1296,6 +1296,7 @@
 {
 	char *filedata;
 	size_t size;
+	struct stat st;
 	GError *err = NULL;
 	GaimConversation *conv;
 	GaimGtkConversation *gtkconv;
@@ -1303,23 +1304,20 @@
 	int id;
 	switch (choice) {
 	case DND_BUDDY_ICON:
-		if (!g_file_get_contents(data->filename, &filedata, &size,
-					 &err)) {
+		if (g_stat(data->filename, &st)) {
 			char *str;
 
-			str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message);
+			str = g_strdup_printf(_("The following error has occurred loading %s: %s"),
+						data->filename, strerror(errno));
 			gaim_notify_error(NULL, NULL,
 					  _("Failed to load image"),
 					  str);
-
-			g_error_free(err);
 			g_free(str);
 
 			return;
 		}
 
-		gaim_buddy_icons_set_for_user(data->account, data->who, filedata, size);
-		g_free(filedata);
+		gaim_gtk_set_custom_buddy_icon(data->account, data->who, data->filename);
 		break;
 	case DND_FILE_TRANSFER:
 		serv_send_file(gaim_account_get_connection(data->account), data->who, data->filename);
@@ -2795,3 +2793,48 @@
 }
 #endif
 
+void gaim_gtk_set_custom_buddy_icon(GaimAccount *account, const char *who, const char *filename)
+{
+	GaimConversation *conv;
+	GaimBuddy *buddy;
+	GaimBlistNode *node;
+	char *path = NULL;
+
+	buddy = gaim_find_buddy(account, who);
+	if (!buddy) {
+		gaim_debug_info("custom-icon", "You can only set custom icon for someone in your buddylist.\n");
+		return;
+	}
+
+	node = (GaimBlistNode*)gaim_buddy_get_contact(buddy);
+	path = (char*)gaim_blist_node_get_string(node, "custom_buddy_icon");
+	if (path) {
+		struct stat st;
+		if (g_stat(path, &st) == 0)
+			g_unlink(path);
+		path = NULL;
+	}
+
+	if (filename) {
+		char *newfile;
+		
+		newfile = gaim_gtk_convert_buddy_icon(gaim_find_prpl(gaim_account_get_protocol_id(account)),
+						filename);
+		path = gaim_buddy_icons_get_full_path(newfile);
+		g_free(newfile);
+	}
+
+	gaim_blist_node_set_string(node, "custom_buddy_icon", path);
+	g_free(path);
+
+	/* Update the conversation */
+	conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, who, account);
+	if (conv)
+		gaim_conversation_update(conv, GAIM_CONV_UPDATE_ICON);
+
+	/* Update the buddylist */
+	if (buddy)
+		gaim_blist_update_buddy_icon(buddy);
+}
+
+
--- a/gtk/gtkutils.h	Sun Oct 29 23:28:37 2006 +0000
+++ b/gtk/gtkutils.h	Sun Oct 29 23:35:57 2006 +0000
@@ -475,7 +475,7 @@
  * 
  * @param plugin     The prpl to conver the icon
  * @param path       The path of a buddy icon to convert
- * @return           The path of a new buddy icon
+ * @return           The name of a new buddy icon
  */
 char* gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path);
 
@@ -491,3 +491,13 @@
 #endif
 
 #endif /* _GAIM_GTKUTILS_H_ */
+
+/**
+ * Set or unset a custom buddyicon for a user.
+ *
+ * @param account   The account the user belongs to.
+ * @param who       The name of the user.
+ * @param filename  The path of the custom icon. If this is @c NULL, then any
+ *                  previously set custom buddy icon for the user is removed.
+ */
+void gaim_gtk_set_custom_buddy_icon(GaimAccount *account, const char *who, const char *filename);