changeset 10439:911530134bf8

[gaim-migrate @ 11697] sf patch #1086253, from Alex Converse closes sf rfe #991372, from Adam Petaccia "New Buddy Search Results Dialog," used for oscar Also some memleak fixes from me. My bad. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 28 Dec 2004 06:24:32 +0000
parents d90ece33e72f
children 0a7f03ee3a61
files COPYRIGHT ChangeLog src/gtkblist.c src/gtknotify.c src/gtksavedstatuses.c src/notify.c src/notify.h src/protocols/oscar/oscar.c
diffstat 8 files changed, 284 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Mon Dec 27 21:24:20 2004 +0000
+++ b/COPYRIGHT	Tue Dec 28 06:24:32 2004 +0000
@@ -35,6 +35,7 @@
 Joe Clarke
 Todd Cohen
 Felipe Contreras
+Alex Converse
 Adam Cowell
 Jeramey Crawford
 Balwinder Singh Dheeman
--- a/ChangeLog	Mon Dec 27 21:24:20 2004 +0000
+++ b/ChangeLog	Tue Dec 28 06:24:32 2004 +0000
@@ -10,6 +10,7 @@
 	* Autoreconnect plugin can hide the reconnect dialog (François Gagné)
 	* Screenname colors in chats now chosen intelligently from GNOME color
 	  palette
+	* New "find buddy" results dialog (Alex Converse)
 	* Yahoo! has the following new "/" commands:  /join, /buzz
 	* Smiley selection dialog rewritten to look nicer (Nathan Fredrickson)
 	* If Gaim is exited with the buddy list hidden in the docklet, it will
--- a/src/gtkblist.c	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/gtkblist.c	Tue Dec 28 06:24:32 2004 +0000
@@ -3186,7 +3186,7 @@
 {
 	gboolean result;
 	gchar *enteredstring;
-	const gchar *withmarkup;
+	gchar *withmarkup;
 	gchar *nomarkup;
 	const gchar *normalized;
 
@@ -3198,6 +3198,7 @@
 
 	result = (g_strstr_len(normalized, strlen(normalized), enteredstring) == NULL);
 
+	g_free(withmarkup);
 	g_free(enteredstring);
 	g_free(nomarkup);
 
--- a/src/gtknotify.c	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/gtknotify.c	Tue Dec 28 06:24:32 2004 +0000
@@ -33,6 +33,7 @@
 #include "gtkstock.h"
 #include "util.h"
 
+#include "gtkblist.h"
 #include "gtkimhtml.h"
 #include "gtknotify.h"
 #include "gtkutils.h"
@@ -46,6 +47,22 @@
 
 } GaimNotifyMailData;
 
+typedef struct
+{
+	GaimAccount *account;
+	GtkListStore *model;
+	GtkWidget *treeview;
+	GtkWidget *window;
+
+} GaimNotifySearchResultsData;
+
+enum
+{
+	COLUMN_ICON,
+	COLUMN_SCREENNAME,
+	NUM_COLUMNS
+};
+
 static void *gaim_gtk_notify_emails(size_t count, gboolean detailed,
 									const char **subjects,
 									const char **froms, const char **tos,
@@ -73,6 +90,33 @@
 	gaim_notify_close(GAIM_NOTIFY_FORMATTED, win);
 }
 
+static void
+searchresults_close_cb(GaimNotifySearchResultsData *data, GdkEvent *event, void *user_data)
+{
+	gaim_notify_close(GAIM_NOTIFY_SEARCHRESULTS, data);
+}
+
+static void
+add_buddy_helper_cb(GtkWidget *widget, GaimNotifySearchResultsData *data)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gchar *buddy;
+
+	g_return_if_fail(data != NULL);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview));
+
+	if (gtk_tree_selection_get_selected(selection, &model, &iter))
+	{
+		gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
+						   COLUMN_SCREENNAME, &buddy, -1);
+		gaim_blist_request_add_buddy(data->account, buddy, NULL, NULL);
+		g_free(buddy);
+	}
+}
+
 static void *
 gaim_gtk_notify_message(GaimNotifyMsgType type, const char *title,
 						const char *primary, const char *secondary,
@@ -378,6 +422,140 @@
 }
 
 static void *
+gaim_gtk_notify_searchresults(GaimConnection *gc, const char *title,
+							  const char *primary, const char *secondary,
+							  const char **results, GCallback cb,
+							  void *user_data)
+{
+	GtkWidget *window;
+	GtkWidget *vbox;
+	GtkWidget *button_area;
+	GtkWidget *label;
+	GtkWidget *close_button;
+	GtkWidget *add_button;
+	GtkWidget *sw;
+	GtkWidget *treeview;
+	GdkPixbuf *icon, *scaled;
+	GaimNotifySearchResultsData *data;
+	GtkListStore *model;
+	GtkCellRenderer *renderer;
+	GtkTreeIter iter;
+	int i;
+	char *label_text;
+
+	data = g_malloc(sizeof(GaimNotifySearchResultsData));
+
+	/* Create the window */
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(window), (title ? title :_("Search Results")));
+	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+	gtk_container_set_border_width(GTK_CONTAINER(window), 12);
+
+	g_signal_connect_swapped(G_OBJECT(window), "delete_event",
+							 G_CALLBACK(searchresults_close_cb), data);
+
+	/* Setup the main vbox */
+	vbox = gtk_vbox_new(FALSE, 12);
+	gtk_container_add(GTK_CONTAINER(window), vbox);
+	gtk_widget_show(vbox);
+
+	/* Setup the descriptive label */
+	label_text = g_strdup_printf(
+			"<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
+			(primary ? primary : ""),
+			(primary && secondary ? "\n" : ""),
+			(secondary ? secondary : ""));
+	label = gtk_label_new(NULL);
+	gtk_label_set_markup(GTK_LABEL(label), label_text);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	gtk_widget_show(label);
+	g_free(label_text);
+
+	/* Setup the list model */
+	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+
+	/* Setup the scrolled window containing the treeview */
+	sw = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+								   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+										GTK_SHADOW_IN);
+	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
+	gtk_widget_show(sw);
+
+	/* Setup the treeview */
+	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
+	gtk_widget_set_size_request(treeview, 250, 150);
+	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
+								GTK_SELECTION_SINGLE);
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
+	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	gtk_widget_show(treeview);
+
+	/* icon column */
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
+					-1, "Icon", renderer,
+					"pixbuf", COLUMN_ICON,
+					NULL);
+
+	/* screenname column */
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
+					-1, "Screenname", renderer,
+					"text", COLUMN_SCREENNAME,
+					NULL);
+
+	/* Setup the button area */
+	button_area = gtk_hbutton_box_new();
+	gtk_box_pack_start(GTK_BOX(vbox), button_area, FALSE, FALSE, 0);
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(button_area), GTK_BUTTONBOX_END);
+	gtk_box_set_spacing(GTK_BOX(button_area), 12);
+	gtk_widget_show(button_area);
+
+	/* Add the Add button */
+	add_button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	gtk_box_pack_start(GTK_BOX(button_area), add_button, FALSE, FALSE, 0);
+	gtk_widget_show(add_button);
+
+	/* Add the Close button */
+	close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	gtk_box_pack_start(GTK_BOX(button_area), close_button, FALSE, FALSE, 0);
+	gtk_widget_show(close_button);
+
+	/* Add the buddies to the tree view */
+	icon = create_prpl_icon(gc->account);
+	scaled = gdk_pixbuf_scale_simple(icon, 16, 16, GDK_INTERP_BILINEAR);
+
+	for (i = 0; results[i] != NULL; i++)
+	{
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+						   COLUMN_ICON, scaled,
+						   COLUMN_SCREENNAME, results[i],
+						   -1);
+	}
+
+	data->account = gc->account;
+	data->model = model;
+	data->treeview = treeview;
+	data->window = window;
+
+	/* Connect Signals */
+	g_signal_connect(G_OBJECT(add_button), "clicked",
+					 G_CALLBACK(add_buddy_helper_cb), data);
+	g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
+							 G_CALLBACK(searchresults_close_cb), data);
+
+	/* Show the window */
+	gtk_widget_show(window);
+	return data;
+}
+
+static void *
 gaim_gtk_notify_userinfo(GaimConnection *gc, const char *who,
 						 const char *title, const char *primary,
 						 const char *secondary, const char *text,
@@ -399,6 +577,14 @@
 		g_free(data->url);
 		g_free(data);
 	}
+	else if (type == GAIM_NOTIFY_SEARCHRESULTS)
+	{
+		GaimNotifySearchResultsData *data = (GaimNotifySearchResultsData *)ui_handle;
+
+		gtk_widget_destroy(data->window);
+
+		g_free(data);
+	}
 	else
 		gtk_widget_destroy(GTK_WIDGET(ui_handle));
 }
@@ -612,6 +798,7 @@
 	gaim_gtk_notify_email,
 	gaim_gtk_notify_emails,
 	gaim_gtk_notify_formatted,
+	gaim_gtk_notify_searchresults,
 	gaim_gtk_notify_userinfo,
 	gaim_gtk_notify_uri,
 	gaim_gtk_close_notify
--- a/src/gtksavedstatuses.c	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/gtksavedstatuses.c	Tue Dec 28 06:24:32 2004 +0000
@@ -89,20 +89,28 @@
 status_window_find_savedstatus(GtkTreeIter *iter, const char *title)
 {
 	GtkTreeModel *model = GTK_TREE_MODEL(status_window->model);
-	const char *cur;
+	char *cur;
 
 	if (!gtk_tree_model_get_iter_first(model, iter))
 		return FALSE;
 
 	gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1);
 	if (!strcmp(title, cur))
+	{
+		g_free(cur);
 		return TRUE;
+	}
+	g_free(cur);
 
 	while (gtk_tree_model_iter_next(model, iter))
 	{
 		gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1);
 		if (!strcmp(title, cur))
+		{
+			g_free(cur);
 			return TRUE;
+		}
+		g_free(cur);
 	}
 
 	return FALSE;
@@ -129,12 +137,13 @@
 status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path,
 							 GtkTreeIter *iter, gpointer user_data)
 {
-	const char *title;
+	char *title;
 	GaimSavedStatus *status;
 
 	gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1);
 
 	status = gaim_savedstatus_find(title);
+	g_free(title);
 	gaim_gtk_status_editor_show(status);
 }
 
@@ -166,7 +175,7 @@
 status_window_delete_foreach(GtkTreeModel *model, GtkTreePath *path,
 							 GtkTreeIter *iter, gpointer user_data)
 {
-	const char *title;
+	char *title;
 	char *title_escaped, *buf;
 
 	gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1);
@@ -174,7 +183,7 @@
 	title_escaped = gaim_escape_html(title);
 	buf = g_strdup_printf(_("Are you sure you want to delete %s?"), title_escaped);
 	free(title_escaped);
-	gaim_request_action(NULL, NULL, buf, NULL, 0, g_strdup(title), 2,
+	gaim_request_action(NULL, NULL, buf, NULL, 0, title, 2,
 						_("Delete"), status_window_delete_confirm_cb,
 						_("Cancel"), g_free);
 	g_free(buf);
@@ -251,11 +260,16 @@
 static gboolean
 search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data)
 {
-	const char *haystack;
+	gboolean result;
+	char *haystack;
 
 	gtk_tree_model_get(model, iter, column, &haystack, -1);
 
-	return (gaim_strcasestr(haystack, key) == NULL);
+	result = (gaim_strcasestr(haystack, key) == NULL);
+
+	g_free(haystack);
+
+	return result;
 }
 
 static GtkWidget *
--- a/src/notify.c	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/notify.c	Tue Dec 28 06:24:32 2004 +0000
@@ -155,8 +155,36 @@
 	return NULL;
 }
 
-void *gaim_notify_userinfo(GaimConnection *gc, const char *who, const char *title,
-						   const char *primary, const char *secondary, 
+void *
+gaim_notify_searchresults(GaimConnection *gc, const char *title,
+						  const char *primary, const char *secondary,
+						  const char **results, GCallback cb, void *user_data)
+{
+	GaimNotifyUiOps *ops;
+
+	ops = gaim_notify_get_ui_ops();
+
+	if (ops != NULL && ops->notify_searchresults != NULL) {
+		GaimNotifyInfo *info;
+
+		info            = g_new0(GaimNotifyInfo, 1);
+		info->type      = GAIM_NOTIFY_SEARCHRESULTS;
+		info->handle    = gc;
+		info->ui_handle = ops->notify_searchresults(gc, title, primary,
+													secondary, results,
+													cb, user_data);
+
+		handles = g_list_append(handles, info);
+
+		return info->ui_handle;
+	}
+
+	return NULL;
+}
+
+void *
+gaim_notify_userinfo(GaimConnection *gc, const char *who, const char *title,
+						   const char *primary, const char *secondary,
 						   const char *text, GCallback cb, void *user_data)
 {
 	GaimNotifyUiOps *ops;
--- a/src/notify.h	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/notify.h	Tue Dec 28 06:24:32 2004 +0000
@@ -36,12 +36,13 @@
  */
 typedef enum
 {
-	GAIM_NOTIFY_MESSAGE = 0, /**< Message notification.         */
-	GAIM_NOTIFY_EMAIL,       /**< Single e-mail notification.   */
-	GAIM_NOTIFY_EMAILS,      /**< Multiple e-mail notification. */
-	GAIM_NOTIFY_FORMATTED,   /**< Formatted text.               */
-	GAIM_NOTIFY_USERINFO,    /**< Formatted userinfo text.      */
-	GAIM_NOTIFY_URI          /**< URI notification or display.  */
+	GAIM_NOTIFY_MESSAGE = 0,   /**< Message notification.         */
+	GAIM_NOTIFY_EMAIL,         /**< Single e-mail notification.   */
+	GAIM_NOTIFY_EMAILS,        /**< Multiple e-mail notification. */
+	GAIM_NOTIFY_FORMATTED,     /**< Formatted text.               */
+	GAIM_NOTIFY_SEARCHRESULTS, /**< Buddy search results.         */
+	GAIM_NOTIFY_USERINFO,      /**< Formatted userinfo text.      */
+	GAIM_NOTIFY_URI            /**< URI notification or display.  */
 
 } GaimNotifyType;
 
@@ -74,6 +75,10 @@
 	void *(*notify_formatted)(const char *title, const char *primary,
 							  const char *secondary, const char *text,
 							  GCallback cb, void *user_data);
+	void *(*notify_searchresults)(GaimConnection *gc, const char *title,
+								  const char *primary, const char *secondary,
+								  const char **results, GCallback cb,
+								  void *user_data);
 	void *(*notify_userinfo)(GaimConnection *gc, const char *who,
 							  const char *title, const char *primary,
 							  const char *secondary, const char *text,
@@ -176,6 +181,28 @@
 							const char *text, GCallback cb, void *user_data);
 
 /**
+ * Displays results from a buddy search.  This can be, for example,
+ * a window with a list of all found buddies, where you are given the
+ * option of adding buddies to your buddy list.
+ *
+ * @param gc        The GaimConnection handle associated with the information.
+ * @param title     The title of the message.  If this is NULL, the title
+ *                  will be "Search	Results."
+ * @param primary   The main point of the message.
+ * @param secondary The secondary information.
+ * @param results   An array of null-terminated buddy names.
+ * @param cb        The callback to call when the user closes
+ *                  the notification.
+ * @param user_data The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ */
+void *gaim_notify_searchresults(GaimConnection *gc, const char *title,
+								const char *primary, const char *secondary,
+								const char **results, GCallback cb,
+								void *user_data);
+
+/**
  * Displays user information with formatted text, passing information giving
  * the connection and username from which the user information came.
  *
--- a/src/protocols/oscar/oscar.c	Mon Dec 27 21:24:20 2004 +0000
+++ b/src/protocols/oscar/oscar.c	Tue Dec 28 06:24:32 2004 +0000
@@ -5197,7 +5197,7 @@
 {
 	GaimConnection *gc = sess->aux_data;
 	gchar *secondary;
-	GString *text;
+	gchar **screennames;
 	int i, num;
 	va_list ap;
 	char *email, *SNs;
@@ -5208,14 +5208,19 @@
 	SNs = va_arg(ap, char *);
 	va_end(ap);
 
+	/* TODO: Need to use ngettext() here */
 	secondary = g_strdup_printf(_("The following screen names are associated with %s"), email);
-	text = g_string_new("");
+
+	screennames = g_malloc((num + 1) * sizeof(gchar *));
 	for (i = 0; i < num; i++)
-		g_string_append_printf(text, "%s<br>", &SNs[i * (MAXSNLEN + 1)]);
-	gaim_notify_formatted(gc, NULL, _("Search Results"), secondary, text->str, NULL, NULL);
+		screennames[i] = g_strdup(&SNs[i * (MAXSNLEN + 1)]);
+	screennames[num] = NULL;
+
+	gaim_notify_searchresults(gc, NULL, NULL, secondary,
+							  (const char **)screennames, NULL, NULL);
 
 	g_free(secondary);
-	g_string_free(text, TRUE);
+	g_strfreev(screennames);
 
 	return 1;
 }