changeset 12136:370f9d7868f9

[gaim-migrate @ 14436] SF Patch #1356575 from Kevin Stange (SimGuy) "This patch moves buddy pounces out of the menu and into a new dialog, as suggested by Sean. I'm not ready to say this is finished, but it's a solid starting point and it does work. I changed the namespacing a little from gaim_gtkpounce to gaim_gtk_pounce to be consistent with the rest of Gaim. I wanted to try to get more information into the pounce manager, but I wasn't sure how to display it. I thought perhaps a column containing a row of icons representing which events are being watched (so the user can see which of several pounces for the same buddy are which), however, while I know how to do this, there aren't icons in Gaim suitable for representing all the events. Like "returned from away" and "idle/unidle", as far as I can see. I'm not sure what else could be shown to make the manager dialog more "informative." The dialog updates automatically to show pounces only for connected accounts and updates when a pounce is added, changed, or removed in some other way than the dialog. I'd like to get feedback on it if anyone has anything they think I should change or fix, I'll do that and update this patch. Otherwise, feel free to commit. :)" As ridingpigs commented in the tracker, this is "far better than the current menu thing." I made a few small changes to this. I believe most of them were related to adding hooks to disable things if there were no accounts connected. I also sorte d the Tools menu a bit and updated the docklet to match. I wish the plugin action code could sort the items it added. committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Fri, 18 Nov 2005 16:37:51 +0000
parents e09bf5bc81d8
children 44104dfce57b
files plugins/ChangeLog.API plugins/docklet/docklet.c src/gtkblist.c src/gtkblist.h src/gtkconv.c src/gtkpounce.c src/gtkpounce.h
diffstat 7 files changed, 517 insertions(+), 140 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/ChangeLog.API	Fri Nov 18 14:57:19 2005 +0000
+++ b/plugins/ChangeLog.API	Fri Nov 18 16:37:51 2005 +0000
@@ -91,6 +91,9 @@
 	* GaimGtkImPane->a_virgin
 	* gaim_str_strip_cr(); use gaim_str_strip_char(str, '\r') instead
 	* gaim_find_buddys_group renamed to gaim_buddy_get_group
+	* gaim_gtkpounce_menu_build()
+	* gaim_gtkpounce_dialog_show()
+	* GaimGtkBuddyList->bpmenu
 
 	Added:
 	* gaim_prefs_disconnect_by_handle()
@@ -146,6 +149,9 @@
 	  with an "unseen" state >= to the specified state
 	* gaim_gtk_create_prpl_icon()
 	* gaim_gtk_create_prpl_icon_with_status()
+	* gaim_gtk_pounces_manager_show()
+	* gaim_gtk_pounces_manager_hide()
+	* gaim_gtk_pounce_editor_show()
 
 	Signals - Changed:  (See the Doxygen docs for details on all signals.)
 	* Signal propagation now stops after a handler returns a non-NULL value.
--- a/plugins/docklet/docklet.c	Fri Nov 18 14:57:19 2005 +0000
+++ b/plugins/docklet/docklet.c	Fri Nov 18 16:37:51 2005 +0000
@@ -306,6 +306,14 @@
 
 	gaim_separator(menu);
 
+	gaim_new_item_from_stock(menu, _("Accounts"), GAIM_STOCK_ACCOUNTS, G_CALLBACK(gaim_gtk_accounts_window_show), NULL, 0, 0, NULL);
+	gaim_new_item_from_stock(menu, _("Plugins"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_plugin_dialog_show), NULL, 0, 0, NULL);
+	gaim_new_item_from_stock(menu, _("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_prefs_show), NULL, 0, 0, NULL);
+
+	gaim_separator(menu);
+
+	gaim_new_item_from_stock(menu, _("File Transfers"), GAIM_STOCK_FILE_TRANSFER, G_CALLBACK(gaim_show_xfer_dialog), NULL, 0, 0, NULL);
+
 	entry = gtk_check_menu_item_new_with_label(_("Mute Sounds"));
 	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), gaim_prefs_get_bool("/gaim/gtk/sound/mute"));
 	if (!strcmp(gaim_prefs_get_string("/gaim/gtk/sound/method"), "none"))
@@ -313,11 +321,6 @@
 	g_signal_connect(G_OBJECT(entry), "toggled", G_CALLBACK(docklet_toggle_mute), NULL);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), entry);
 
-	gaim_new_item_from_stock(menu, _("File Transfers"), GAIM_STOCK_FILE_TRANSFER, G_CALLBACK(gaim_show_xfer_dialog), NULL, 0, 0, NULL);
-	gaim_new_item_from_stock(menu, _("Accounts"), GAIM_STOCK_ACCOUNTS, G_CALLBACK(gaim_gtk_accounts_window_show), NULL, 0, 0, NULL);
-	gaim_new_item_from_stock(menu, _("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_prefs_show), NULL, 0, 0, NULL);
-	gaim_new_item_from_stock(menu, _("Plugins"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_plugin_dialog_show), NULL, 0, 0, NULL);
-
 	gaim_separator(menu);
 
 	/* TODO: need a submenu to change status, this needs to "link" 
--- a/src/gtkblist.c	Fri Nov 18 14:57:19 2005 +0000
+++ b/src/gtkblist.c	Fri Nov 18 16:37:51 2005 +0000
@@ -340,7 +340,7 @@
 
 static void gtk_blist_menu_bp_cb(GtkWidget *w, GaimBuddy *b)
 {
-	gaim_gtkpounce_dialog_show(b->account, b->name, NULL);
+	gaim_gtk_pounce_editor_show(b->account, b->name, NULL);
 }
 
 static void gtk_blist_menu_showlog_cb(GtkWidget *w, GaimBlistNode *node)
@@ -2438,18 +2438,19 @@
 
 	/* Tools */
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" },
 	{ N_("/Tools/Account Ac_tions"), NULL, NULL, 0, "<Branch>" },
 	{ "/Tools/sep1", NULL, NULL, 0, "<Separator>" },
 	{ N_("/Tools/A_ccounts"), "<CTL>A", gaim_gtk_accounts_window_show, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS },
-	{ N_("/Tools/Pr_eferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
+	{ N_("/Tools/Buddy _Pounces"), NULL, gaim_gtk_pounces_manager_show, 0, NULL },
 	{ N_("/Tools/Plu_gins"), "<CTL>U", gaim_gtk_plugin_dialog_show, 0, NULL },
+	{ N_("/Tools/Pr_eferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
 	{ N_("/Tools/Pr_ivacy"), NULL, gaim_gtk_privacy_dialog_show, 0, "<StockItem>", GTK_STOCK_DIALOG_ERROR },
+	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>" },
 	{ N_("/Tools/_File Transfers"), "<CTL>T", gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER },
 	{ N_("/Tools/R_oom List"), NULL, gaim_gtk_roomlist_dialog_show, 0, "<StockItem>", GTK_STOCK_INDEX },
-	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL },
+	{ "/Tools/sep3", NULL, NULL, 0, "<Separator>" },
 	{ N_("/Tools/Mute _Sounds"), "<CTL>S", gaim_gtk_blist_mute_sounds_cb, 0, "<CheckItem>"},
-	{ N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL },
 
 	/* Help */
 	{ N_("/_Help"), NULL, NULL, 0, "<Branch>" },
@@ -3086,7 +3087,6 @@
 	g_return_if_fail(gtkblist != NULL);
 
 	gaim_gtk_blist_update_protocol_actions();
-	gaim_gtkpounce_menu_build(gtkblist->bpmenu);
 
 	sensitive = (gaim_connections_get_all() != NULL);
 
@@ -3102,11 +3102,14 @@
 	widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Add Chat..."));
 	gtk_widget_set_sensitive(widget, gaim_gtk_blist_joinchat_is_showable());
 
-	widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List"));
-	gtk_widget_set_sensitive(widget, gaim_gtk_roomlist_is_showable());
+	widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounces"));
+	gtk_widget_set_sensitive(widget, (gaim_connections_get_all() != NULL));
 
 	widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Privacy"));
 	gtk_widget_set_sensitive(widget, gaim_gtk_privacy_is_showable());
+
+	widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List"));
+	gtk_widget_set_sensitive(widget, gaim_gtk_roomlist_is_showable());
 }
 
 static void
@@ -3448,7 +3451,6 @@
 	gtk_widget_show(menu);
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), menu, FALSE, FALSE, 0);
 
-	gtkblist->bpmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounce"));
 	protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Account Actions"));
 
 	/****************************** GtkVPaned ************************************/
@@ -5249,7 +5251,7 @@
 }
 
 static GList *plugin_menu_items = NULL;
-static int plugin_menu_index = 8;
+static int plugin_menu_index = 10;
 
 static void
 build_plugin_actions(GtkWidget *menu, GaimPlugin *plugin, gpointer context)
--- a/src/gtkblist.h	Fri Nov 18 14:57:19 2005 +0000
+++ b/src/gtkblist.h	Fri Nov 18 16:37:51 2005 +0000
@@ -68,7 +68,6 @@
 	GtkCellRenderer *text_rend;
 
 	GtkItemFactory *ift;
-	GtkWidget *bpmenu;              /**< The buddy pounce menu. */
 	GtkWidget *menutray;            /**< The menu tray widget. */
 	GtkWidget *menutrayicon;        /**< The menu tray icon. */
 
--- a/src/gtkconv.c	Fri Nov 18 14:57:19 2005 +0000
+++ b/src/gtkconv.c	Fri Nov 18 16:37:51 2005 +0000
@@ -1093,8 +1093,8 @@
 
 	conv = gaim_gtk_conv_window_get_active_gtkconv(win)->active_conv;
 
-	gaim_gtkpounce_dialog_show(gaim_conversation_get_account(conv),
-	                           gaim_conversation_get_name(conv), NULL);
+	gaim_gtk_pounce_editor_show(gaim_conversation_get_account(conv),
+								gaim_conversation_get_name(conv), NULL);
 }
 
 static void
--- a/src/gtkpounce.c	Fri Nov 18 14:57:19 2005 +0000
+++ b/src/gtkpounce.c	Fri Nov 18 16:37:51 2005 +0000
@@ -39,8 +39,24 @@
 #include "gtkblist.h"
 #include "gtkdialogs.h"
 #include "gtkpounce.h"
+#include "gtkstock.h"
 #include "gtkutils.h"
 
+/**
+ * These are used for the GtkTreeView when you're scrolling through
+ * all your saved pounces.
+ */
+enum
+{
+	/* Hidden column containing the GaimPounce */
+	POUNCES_MANAGER_COLUMN_POUNCE,
+	POUNCES_MANAGER_COLUMN_ICON,
+	POUNCES_MANAGER_COLUMN_TARGET,
+	POUNCES_MANAGER_COLUMN_ACCOUNT,
+	POUNCES_MANAGER_COLUMN_PERSISTENCE,
+	POUNCES_MANAGER_NUM_COLUMNS
+};
+
 typedef struct
 {
 	/* Pounce data */
@@ -84,6 +100,17 @@
 
 } GaimGtkPounceDialog;
 
+typedef struct
+{
+	GtkWidget *window;
+	GtkListStore *model;
+	GtkWidget *treeview;
+	GtkWidget *modify_button;
+	GtkWidget *delete_button;
+} PouncesManager;
+
+static PouncesManager *pounces_manager = NULL;
+
 /**************************************************************************
  * Callbacks
  **************************************************************************/
@@ -145,12 +172,78 @@
 }
 
 static void
+add_pounce_to_treeview(GtkListStore *model, GaimPounce *pounce)
+{
+	GtkTreeIter iter;
+	GaimAccount *account;
+	GaimPounceEvent events;
+	gboolean persists;
+	const char *pouncer;
+	const char *pouncee;
+	GdkPixbuf *pixbuf, *scale = NULL;
+
+	account = gaim_pounce_get_pouncer(pounce);
+
+	if (gaim_account_is_disconnected(account))
+		return;
+
+	events = gaim_pounce_get_events(pounce);
+
+	pixbuf = gaim_gtk_create_prpl_icon(account);
+
+	if (pixbuf != NULL)
+		scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
+										GDK_INTERP_BILINEAR);
+
+	pouncer = gaim_account_get_username(account);
+	pouncee = gaim_pounce_get_pouncee(pounce);
+	persists = gaim_pounce_get_save(pounce);
+
+	gtk_list_store_append(model, &iter);
+	gtk_list_store_set(model, &iter,
+					   POUNCES_MANAGER_COLUMN_POUNCE, pounce,
+					   POUNCES_MANAGER_COLUMN_ICON, scale,
+					   POUNCES_MANAGER_COLUMN_TARGET, pouncee,
+					   POUNCES_MANAGER_COLUMN_ACCOUNT, pouncer,
+					   POUNCES_MANAGER_COLUMN_PERSISTENCE, persists,
+					   -1);
+}
+
+static void
+populate_pounces_list(PouncesManager *dialog)
+{
+	const GList *pounces;
+
+	gtk_list_store_clear(dialog->model);
+
+	for (pounces = gaim_pounces_get_all(); pounces != NULL;
+			pounces = g_list_next(pounces))
+	{
+		add_pounce_to_treeview(dialog->model, pounces->data);
+	}
+}
+
+static void
+update_pounces(void)
+{
+	/* Rebuild the pounces list if the pounces manager is open */
+	if (pounces_manager != NULL)
+	{
+		populate_pounces_list(pounces_manager);
+	}
+}
+
+static void
+signed_on_off_cb(GaimConnection *gc, gpointer user_data)
+{
+	update_pounces();
+}
+
+static void
 save_pounce_cb(GtkWidget *w, GaimGtkPounceDialog *dialog)
 {
 	const char *name;
 	const char *message, *command, *sound;
-	GaimBuddyList *blist;
-	GaimGtkBuddyList *gtkblist;
 	GaimPounceEvent events = GAIM_POUNCE_NONE;
 
 	name = gtk_entry_get_text(GTK_ENTRY(dialog->buddy_entry));
@@ -241,17 +334,9 @@
 	gaim_pounce_set_save(dialog->pounce,
 		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->save_pounce)));
 
-	delete_win_cb(NULL, NULL, dialog);
-
-	/* Rebuild the pounce menu */
-	blist = gaim_get_blist();
+	update_pounces();
 
-	if (GAIM_IS_GTK_BLIST(blist))
-	{
-		gtkblist = GAIM_GTK_BLIST(blist);
-
-		gaim_gtkpounce_menu_build(gtkblist->bpmenu);
-	}
+	delete_win_cb(NULL, NULL, dialog);
 }
 
 static void
@@ -338,8 +423,8 @@
 };
 
 void
-gaim_gtkpounce_dialog_show(GaimAccount *account, const char *name,
-						   GaimPounce *cur_pounce)
+gaim_gtk_pounce_editor_show(GaimAccount *account, const char *name,
+							GaimPounce *cur_pounce)
 {
 	GaimGtkPounceDialog *dialog;
 	GtkWidget *window;
@@ -797,135 +882,399 @@
 	gtk_widget_show(window);
 }
 
-static void
-new_pounce_cb(GtkWidget *w, GaimBuddy *b)
+static gboolean
+pounces_manager_configure_cb(GtkWidget *widget, GdkEventConfigure *event, PouncesManager *dialog)
+{
+	if (GTK_WIDGET_VISIBLE(widget)) {
+		gaim_prefs_set_int("/gaim/gtk/pounces/dialog/width",  event->width);
+		gaim_prefs_set_int("/gaim/gtk/pounces/dialog/height", event->height);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+pounces_manager_find_pounce(GtkTreeIter *iter, GaimPounce *pounce)
 {
-	if (b == NULL)
-		gaim_gtkpounce_dialog_show(NULL, NULL, NULL);
-	else
-		gaim_gtkpounce_dialog_show(b->account, b->name, NULL);
+	GtkTreeModel *model = GTK_TREE_MODEL(pounces_manager->model);
+	GaimPounce *p;
+
+	if (!gtk_tree_model_get_iter_first(model, iter))
+		return FALSE;
+
+	gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &p, -1);
+	if (pounce == p)
+		return TRUE;
+
+	while (gtk_tree_model_iter_next(model, iter))
+	{
+		gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &p, -1);
+		if (pounce == p)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+pounces_manager_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+	PouncesManager *dialog = user_data;
+
+	dialog->window = NULL;
+	gaim_gtk_pounces_manager_hide();
+
+	return FALSE;
+}
+
+#if !GTK_CHECK_VERSION(2,2,0)
+static void
+count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	(*(gint *)user_data)++;
+}
+#endif
+
+static void
+pounces_manager_connection_cb(GaimConnection *gc, GtkWidget *add_button)
+{
+	gtk_widget_set_sensitive(add_button, (gaim_connections_get_all() != NULL));
 }
 
 static void
-delete_pounce_cb(GtkWidget *w, GaimPounce *pounce)
+pounces_manager_add_cb(GtkButton *button, gpointer user_data)
+{
+	gaim_gtk_pounce_editor_show(NULL, NULL, NULL);
+}
+
+static void
+pounces_manager_modify_foreach(GtkTreeModel *model, GtkTreePath *path,
+							 GtkTreeIter *iter, gpointer user_data)
 {
+	GaimPounce *pounce;
+
+	gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
+	gaim_gtk_pounce_editor_show(NULL, NULL, pounce);
+}
+
+static void
+pounces_manager_modify_cb(GtkButton *button, gpointer user_data)
+{
+	PouncesManager *dialog = user_data;
+	GtkTreeSelection *selection;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
+
+	gtk_tree_selection_selected_foreach(selection, pounces_manager_modify_foreach, user_data);
+}
+
+static void
+pounces_manager_delete_confirm_cb(GaimPounce *pounce)
+{
+	GtkTreeIter iter;
+
+	if (pounces_manager_find_pounce(&iter, pounce))
+		gtk_list_store_remove(pounces_manager->model, &iter);
+
 	gaim_pounce_destroy(pounce);
 }
 
 static void
-edit_pounce_cb(GtkWidget *w, GaimPounce *pounce)
+pounces_manager_delete_foreach(GtkTreeModel *model, GtkTreePath *path,
+							   GtkTreeIter *iter, gpointer user_data)
+{
+	GaimPounce *pounce;
+	GaimAccount *account;
+	const char *pouncer, *pouncee;
+	char *buf;
+
+	gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
+	account = gaim_pounce_get_pouncer(pounce);
+	pouncer = gaim_account_get_username(account);
+	pouncee = gaim_pounce_get_pouncee(pounce);
+
+	buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
+	gaim_request_action(NULL, NULL, buf, NULL, 0, pounce, 2,
+						_("Delete"), pounces_manager_delete_confirm_cb,
+						_("Cancel"), g_free);
+	g_free(buf);
+}
+
+static void
+pounces_manager_delete_cb(GtkButton *button, gpointer user_data)
+{
+	PouncesManager *dialog = user_data;
+	GtkTreeSelection *selection;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
+
+	gtk_tree_selection_selected_foreach(selection, pounces_manager_delete_foreach, user_data);
+}
+
+static void
+pounces_manager_close_cb(GtkButton *button, gpointer user_data)
 {
-	gaim_gtkpounce_dialog_show(NULL, NULL, pounce);
+	gaim_gtk_pounces_manager_hide();
+}
+
+static void
+pounce_selected_cb(GtkTreeSelection *sel, gpointer user_data)
+{
+	PouncesManager *dialog = user_data;
+	int num_selected = 0;
+
+#if GTK_CHECK_VERSION(2,2,0)
+	num_selected = gtk_tree_selection_count_selected_rows(sel);
+#else
+	gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected);
+#endif
+
+	gtk_widget_set_sensitive(dialog->modify_button, (num_selected > 0));
+	gtk_widget_set_sensitive(dialog->delete_button, (num_selected > 0));
+}
+
+static void
+pounces_manager_persists_cb(GtkCellRendererToggle *renderer, gchar *path_str,
+							gpointer user_data)
+{
+	PouncesManager *dialog = user_data;
+	GaimPounce *pounce;
+	gboolean persists;
+	GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
+	GtkTreeIter iter;
+
+	gtk_tree_model_get_iter_from_string(model, &iter, path_str);
+	gtk_tree_model_get(model, &iter,
+					   POUNCES_MANAGER_COLUMN_POUNCE, &pounce,
+					   POUNCES_MANAGER_COLUMN_PERSISTENCE, &persists,
+					   -1);
+
+	gaim_pounce_set_save(pounce, !persists);
+
+	update_pounces();
 }
 
 static gboolean
-fill_menu(GtkWidget *menu, GCallback cb)
+search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data)
 {
-	GtkWidget *image;
-	GtkWidget *item;
-	GdkPixbuf *pixbuf, *scale;
-	GaimPounce *pounce;
-	GaimBuddy *buddy;
-	const char *buddyname;
-	gboolean has_items = FALSE;
-	GList *bp;
+	gboolean result;
+	char *haystack;
+
+	gtk_tree_model_get(model, iter, column, &haystack, -1);
+
+	result = (gaim_strcasestr(haystack, key) == NULL);
+
+	g_free(haystack);
+
+	return result;
+}
+
+static GtkWidget *
+create_pounces_list(PouncesManager *dialog)
+{
+	GtkWidget *sw;
+	GtkWidget *treeview;
+	GtkTreeSelection *sel;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
 
-	for (bp = gaim_pounces_get_all(); bp != NULL; bp = bp->next)
-	{
-		pounce = (GaimPounce *)bp->data;
-
-		/* Check if account is online, if not skip it */
-		if (!gaim_account_is_connected(gaim_pounce_get_pouncer(pounce)))
-			continue;
+	/* Create the scrolled window */
+	sw = gtk_scrolled_window_new(0, 0);
+	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_widget_show(sw);
 
-		buddy = gaim_find_buddy(gaim_pounce_get_pouncer(pounce), gaim_pounce_get_pouncee(pounce));
+	/* Create the list model */
+	dialog->model = gtk_list_store_new(POUNCES_MANAGER_NUM_COLUMNS,
+									   G_TYPE_POINTER,
+									   GDK_TYPE_PIXBUF,
+									   G_TYPE_STRING,
+									   G_TYPE_STRING,
+									   G_TYPE_BOOLEAN
+									   );
 
-		if (buddy != NULL)
-			buddyname = gaim_buddy_get_contact_alias(buddy);
-		else
-			buddyname = gaim_pounce_get_pouncee(pounce);
+	/* Create the treeview */
+	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
+	dialog->treeview = treeview;
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
 
-		has_items = TRUE;
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+	gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
+	g_signal_connect(G_OBJECT(sel), "changed",
+					 G_CALLBACK(pounce_selected_cb), dialog);
+
+	gtk_container_add(GTK_CONTAINER(sw), treeview);
+	gtk_widget_show(treeview);
 
-		/* Build the menu item */
-		item = gtk_image_menu_item_new_with_label(buddyname);
+	/* Pouncee Column */
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_title(column, _("Pounce Target"));
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_column_set_min_width(column, 200);
+	gtk_tree_view_column_set_sort_column_id(column,
+											POUNCES_MANAGER_COLUMN_TARGET);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
-		/* Create a pixmap for the protocol icon. */
-		pixbuf = gaim_gtk_create_prpl_icon(gaim_pounce_get_pouncer(pounce));
-		if (pixbuf != NULL)
-		{
-			scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
-											GDK_INTERP_BILINEAR);
+	/* Icon */
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(column, renderer, FALSE);
+	gtk_tree_view_column_add_attribute(column, renderer, "pixbuf",
+									   POUNCES_MANAGER_COLUMN_ICON);
+
+	/* Pouncee */
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column, renderer, TRUE);
+	gtk_tree_view_column_add_attribute(column, renderer, "text",
+									   POUNCES_MANAGER_COLUMN_TARGET);
+
 
-			/* Now convert it to GtkImage */
-			image = gtk_image_new_from_pixbuf(scale);
-			g_object_unref(G_OBJECT(scale));
-			g_object_unref(G_OBJECT(pixbuf));
-			gtk_widget_show(image);
-			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
-		}
+	/* Account Column */
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_title(column, _("Account"));
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_column_set_min_width(column, 200);
+	gtk_tree_view_column_set_sort_column_id(column,
+											POUNCES_MANAGER_COLUMN_ACCOUNT);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column, renderer, TRUE);
+	gtk_tree_view_column_add_attribute(column, renderer, "text",
+									   POUNCES_MANAGER_COLUMN_ACCOUNT);
 
-		/* Put the item in the menu */
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-		gtk_widget_show(item);
+	/* Persistence Column */
+	renderer = gtk_cell_renderer_toggle_new();
+	column = gtk_tree_view_column_new_with_attributes(_("Persists"), renderer,
+						"active", POUNCES_MANAGER_COLUMN_PERSISTENCE, NULL);
+	gtk_tree_view_column_set_sort_column_id(column,
+											POUNCES_MANAGER_COLUMN_PERSISTENCE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+	g_signal_connect(G_OBJECT(renderer), "toggled",
+			 G_CALLBACK(pounces_manager_persists_cb), dialog);
 
-		/* Set our callbacks. */
-		g_signal_connect(G_OBJECT(item), "activate", cb, pounce);
-	}
+	/* Enable CTRL+F searching */
+	gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), POUNCES_MANAGER_COLUMN_TARGET);
+	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), search_func, NULL, NULL);
 
-	return has_items;
+	/* Sort the pouncee column by default */
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model),
+										 POUNCES_MANAGER_COLUMN_TARGET,
+										 GTK_SORT_ASCENDING);
+
+	/* Populate list */
+	populate_pounces_list(dialog);
+
+	return sw;
 }
 
 void
-gaim_gtkpounce_menu_build(GtkWidget *menu)
+gaim_gtk_pounces_manager_show(void)
 {
-	GtkWidget *remmenu;
-	GtkWidget *item;
-	GList *children, *l;
-	gboolean has_items;
+	PouncesManager *dialog;
+	GtkWidget *bbox;
+	GtkWidget *button;
+	GtkWidget *list;
+	GtkWidget *vbox;
+	GtkWidget *win;
+	int width, height;
 
-	g_return_if_fail(menu != NULL);
-
-	if ((children = gtk_container_get_children(GTK_CONTAINER(menu))) != NULL)
-	{
-		for (l = children; l != NULL; l = l->next)
-			gtk_widget_destroy(GTK_WIDGET(l->data));
-
-		g_list_free(children);
+	if (pounces_manager != NULL) {
+		gtk_window_present(GTK_WINDOW(pounces_manager->window));
+		return;
 	}
 
-	/* "New Buddy Pounce" */
-	item = gtk_menu_item_new_with_label(_("New Buddy Pounce"));
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-	gtk_widget_set_sensitive(item, (gaim_connections_get_all() != NULL));
-	gtk_widget_show(item);
-	g_signal_connect(G_OBJECT(item), "activate",
-					 G_CALLBACK(new_pounce_cb), NULL);
+	pounces_manager = dialog = g_new0(PouncesManager, 1);
+
+	width  = gaim_prefs_get_int("/gaim/gtk/pounces/dialog/width");
+	height = gaim_prefs_get_int("/gaim/gtk/pounces/dialog/height");
+
+	dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
+	gtk_window_set_role(GTK_WINDOW(win), "pounces");
+	gtk_window_set_title(GTK_WINDOW(win), _("Buddy Pounces"));
+	gtk_container_set_border_width(GTK_CONTAINER(win), GAIM_HIG_BORDER);
+
+	g_signal_connect(G_OBJECT(win), "delete_event",
+					 G_CALLBACK(pounces_manager_destroy_cb), dialog);
+	g_signal_connect(G_OBJECT(win), "configure_event",
+					 G_CALLBACK(pounces_manager_configure_cb), dialog);
+
+	/* Setup the vbox */
+	vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER);
+	gtk_container_add(GTK_CONTAINER(win), vbox);
+	gtk_widget_show(vbox);
 
-	/* "Remove Buddy Pounce" */
-	item = gtk_menu_item_new_with_label(_("Remove Buddy Pounce"));
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+	/* List of saved buddy pounces */
+	list = create_pounces_list(dialog);
+	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
+
+	/* Button box. */
+	bbox = gtk_hbutton_box_new();
+	gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE);
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
+	gtk_widget_show(bbox);
 
-	/* "Remove Buddy Pounce" menu */
-	remmenu = gtk_menu_new();
+	/* Add button */
+	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive(button, (gaim_connections_get_all() != NULL));
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
+						pounces_manager, GAIM_CALLBACK(pounces_manager_connection_cb), button);
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
+						pounces_manager, GAIM_CALLBACK(pounces_manager_connection_cb), button);
+	gtk_widget_show(button);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+					 G_CALLBACK(pounces_manager_add_cb), dialog);
 
-	has_items = fill_menu(remmenu, G_CALLBACK(delete_pounce_cb));
+	/* Modify button */
+	button = gtk_button_new_from_stock(GAIM_STOCK_MODIFY);
+	dialog->modify_button = button;
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive(button, FALSE);
+	gtk_widget_show(button);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+					 G_CALLBACK(pounces_manager_modify_cb), dialog);
 
-	if (!has_items)
-		gtk_widget_set_sensitive(item, FALSE);
+	/* Delete button */
+	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	dialog->delete_button = button;
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive(button, FALSE);
+	gtk_widget_show(button);
 
-	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), remmenu);
-	gtk_widget_show(remmenu);
-	gtk_widget_show(item);
+	g_signal_connect(G_OBJECT(button), "clicked",
+					 G_CALLBACK(pounces_manager_delete_cb), dialog);
 
-	/* Separator */
-	if (has_items)
-	{
-		item = gtk_separator_menu_item_new();
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-		gtk_widget_show(item);
-	}
-	fill_menu(menu, G_CALLBACK(edit_pounce_cb));
+	/* Close button */
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	gtk_widget_show(button);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+					 G_CALLBACK(pounces_manager_close_cb), dialog);
+
+	gtk_widget_show(win);
+}
+
+void
+gaim_gtk_pounces_manager_hide(void)
+{
+	if (pounces_manager == NULL)
+		return;
+
+	if (pounces_manager->window != NULL)
+		gtk_widget_destroy(pounces_manager->window);
+
+	gaim_signals_disconnect_by_handle(pounces_manager);
+
+	g_free(pounces_manager);
+	pounces_manager = NULL;
 }
 
 static void
@@ -1113,18 +1462,7 @@
 static void
 free_pounce(GaimPounce *pounce)
 {
-	GaimBuddyList *blist;
-	GaimGtkBuddyList *gtkblist;
-
-	/* Rebuild the pounce menu */
-	blist = gaim_get_blist();
-
-	if (GAIM_IS_GTK_BLIST(blist))
-	{
-		gtkblist = GAIM_GTK_BLIST(blist);
-
-		gaim_gtkpounce_menu_build(gtkblist->bpmenu);
-	}
+	update_pounces();
 }
 
 static void
@@ -1135,6 +1473,15 @@
 	gaim_pounce_action_register(pounce, "send-message");
 	gaim_pounce_action_register(pounce, "execute-command");
 	gaim_pounce_action_register(pounce, "play-sound");
+
+	update_pounces();
+}
+
+void *
+gaim_gtk_pounces_get_handle() {
+	static int handle;
+
+	return &handle;
 }
 
 void
@@ -1155,4 +1502,14 @@
 						FALSE);
 	gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/play-sound",
 						FALSE);
+	gaim_prefs_add_none("/gaim/gtk/pounces/dialog");
+	gaim_prefs_add_int("/gaim/gtk/pounces/dialog/width",  550);
+	gaim_prefs_add_int("/gaim/gtk/pounces/dialog/height", 250);
+
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
+						gaim_gtk_pounces_get_handle(),
+						GAIM_CALLBACK(signed_on_off_cb), NULL);
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
+						gaim_gtk_pounces_get_handle(),
+						GAIM_CALLBACK(signed_on_off_cb), NULL);
 }
--- a/src/gtkpounce.h	Fri Nov 18 14:57:19 2005 +0000
+++ b/src/gtkpounce.h	Fri Nov 18 16:37:51 2005 +0000
@@ -34,15 +34,25 @@
  * @param name       The optional name to pounce on.
  * @param cur_pounce The current buddy pounce, if editing an existing one.
  */
-void gaim_gtkpounce_dialog_show(GaimAccount *account, const char *name,
+void gaim_gtk_pounce_editor_show(GaimAccount *account, const char *name,
 								GaimPounce *cur_pounce);
 
 /**
- * Displays all registered buddy pounces in a menu.
+ * Shows the pounces manager window.
+ */
+void gaim_gtk_pounces_manager_show(void);
+
+/**
+ * Hides the pounces manager window.
+ */
+void gaim_gtk_pounces_manager_hide(void);
+
+/**
+ * Returns the gtkaccounts handle
  *
- * @param menu The menu to add to.
+ * @return The handle to the GTK+ account system
  */
-void gaim_gtkpounce_menu_build(GtkWidget *menu);
+void *gaim_gtk_pounces_get_handle(void);
 
 /**
  * Initializes the GTK+ pounces subsystem.