changeset 4916:d9b6b5ae34e4

[gaim-migrate @ 5250] Buddy list editing. Does this work? I don't know; I don't test things. It compiles though. It probably does work though, because I'm perfect. So, see, I did really terribly in school last semester (really terribly-- like, why didn't they kick me out terribly) and so I'm working really hard to do well this semester (and I am so far :)). Anyway, that's why you may have noticed I'm a bit slow with the development of late. In fact, I would test and fix this stuff up, but I really need to work on an English paper, so I figured it'd be best just to commit it as is and let Rob, Nathan, Chip and the boys work out the kinks. Besides, I've had most of this code written for weeks already. Thank you all for your patience. Oh, so there's now an Edit menu on your buddy list (which makes the minimum buddy list width wider :-D) and here you'll find things with which to edit your list and privacy, prefs and accounts. It should all be real intuitive. Feel free to IM me if you want to talk about my paper. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Mon, 31 Mar 2003 07:19:46 +0000
parents 0230df73f56a
children c0c6efda8151
files pixmaps/status/default/Makefile.am pixmaps/status/default/offline.png src/buddy.c src/dialogs.c src/gtklist.h src/protocols/gg/gg.c src/protocols/irc/irc.c src/protocols/jabber/jabber.c src/protocols/msn/msn.c src/protocols/napster/napster.c src/protocols/oscar/oscar.c src/protocols/toc/toc.c src/protocols/yahoo/yahoo.c src/prpl.h
diffstat 14 files changed, 348 insertions(+), 218 deletions(-) [+]
line wrap: on
line diff
--- a/pixmaps/status/default/Makefile.am	Mon Mar 31 02:38:44 2003 +0000
+++ b/pixmaps/status/default/Makefile.am	Mon Mar 31 07:19:46 2003 +0000
@@ -1,7 +1,7 @@
 EXTRA_DIST = login.png aim.png icq.png msn.png jabber.png yahoo.png gadu-gadu.png napster.png irc.png \
-	     activebuddy.png admin.png aol.png away.png wireless.png
+	     activebuddy.png admin.png aol.png away.png wireless.png offline.png
 
 gaimstatuspixdir = $(datadir)/pixmaps/gaim/status/default
 
 gaimstatuspix_DATA = login.png aim.png icq.png msn.png jabber.png yahoo.png gadu-gadu.png napster.png irc.png \
-	   	     activebuddy.png admin.png aol.png away.png wireless.png
+	   	     activebuddy.png admin.png aol.png away.png wireless.png offline.png
Binary file pixmaps/status/default/offline.png has changed
--- a/src/buddy.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/buddy.c	Mon Mar 31 07:19:46 2003 +0000
@@ -165,7 +165,7 @@
 
 static void gtk_blist_show_onlinehelp_cb()
 {
-       open_url(NULL, "http://gaim.sourceforge.net/documentation.php");
+       open_url(NULL, WEBSITE "documentation.php");
 }
 
 static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv)
@@ -239,6 +239,35 @@
 	}
 }
 
+static void gaim_gtk_blist_add_buddy_cb()
+{
+	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
+	GtkTreeIter iter;
+	GaimBlistNode *node;
+
+	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
+		if (GAIM_BLIST_NODE_IS_BUDDY(node)) 
+			show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL);
+		else if (GAIM_BLIST_NODE_IS_GROUP(node))
+			show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL);
+	}
+	else {
+		show_add_buddy(NULL, NULL, NULL, NULL);
+	}
+}
+
+gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) 
+{
+	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		struct buddy *b = (struct buddy*)node;
+		show_confirm_del(b->account->gc, b->name);
+	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		struct group *g = (struct group*)node;
+		show_confirm_del_group(g);
+	}
+}
+
 static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b)
 {
 	struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
@@ -267,53 +296,74 @@
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
 	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
 	node = g_value_get_pointer(&val);
-
-	if (!GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		gtk_tree_path_free(path);
-		return FALSE;
-	}
-
 	menu = gtk_menu_new();
 
-	/* Protocol specific options */
-	prpl = find_prpl(((struct buddy*)node)->account->protocol);
+	if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Add a Buddy"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node);
+		image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
+		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+		
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Delete Group"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_gtk_blist_remove_cb), node);
+		image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
+		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Rename"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(show_rename_group), node);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	} else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
+		
+		/* Protocol specific options */
+		prpl = find_prpl(((struct buddy*)node)->account->protocol);
+
+		if(prpl && prpl->get_info) {
+			menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Get Info"));
+			g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_info_cb), node);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+		}
 
-	if(prpl && prpl->get_info) {
-		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Get Info"));
-		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_info_cb), node);
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_IM"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_im_cb), node);
+		image = gtk_image_new_from_stock(GAIM_STOCK_IM, GTK_ICON_SIZE_MENU);
+		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("Add Buddy _Pounce"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_bp_cb), node);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("View _Log"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_showlog_cb), node);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		if (prpl) {
+			list = prpl->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
+			while (list) {
+				struct proto_buddy_menu *pbm = list->data;
+				menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
+				g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
+				g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node);
+				gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+				list = list->next;
+			}
+		}
+	
+		gaim_separator(menu);
+	
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Alias"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_alias_cb), node);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Remove"));
+		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_gtk_blist_remove_cb), node);
+		image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
+		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 	}
-
-	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_IM"));
-	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_im_cb), node);
-	image = gtk_image_new_from_stock(GAIM_STOCK_IM, GTK_ICON_SIZE_MENU);
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Alias"));
-	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_alias_cb), node);
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-	menuitem = gtk_image_menu_item_new_with_mnemonic(_("Add Buddy _Pounce"));
-	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_bp_cb), node);
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-	menuitem = gtk_image_menu_item_new_with_mnemonic(_("View _Log"));
-	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_blist_menu_showlog_cb), node);
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-	if (prpl) {
-		list = prpl->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
-		while (list) {
-			struct proto_buddy_menu *pbm = list->data;
-			menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
-			g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
-			g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node);
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-			list = list->next;
-		}
-	}
-
+	
 	gtk_widget_show_all(menu);
 	
 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
@@ -336,24 +386,19 @@
 	gaim_gtk_blist_update(NULL, b);
 	return FALSE;
 }
-
-static void gaim_gtk_blist_add_buddy_cb()
-{
-	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
-	GtkTreeIter iter;
-	GaimBlistNode *node;
+static void edit_mode_cb() {
+	GdkCursor *cursor = gdk_cursor_new(GDK_WATCH);
+	gdk_window_set_cursor(gtkblist->window->window, cursor);
+	while (gtk_events_pending())
+		gtk_main_iteration();
+	gtkblist->editmode = !gtkblist->editmode;
+	gdk_cursor_unref(cursor);
+	cursor = gdk_cursor_new(GDK_LEFT_PTR);
+	gdk_window_set_cursor(gtkblist->window->window, cursor);
+	gdk_cursor_unref(cursor);
+	gaim_gtk_blist_refresh(gaim_get_blist());
+}
 
-	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
-		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
-		if (GAIM_BLIST_NODE_IS_BUDDY(node)) 
-			show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL);
-		else if (GAIM_BLIST_NODE_IS_GROUP(node))
-			show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL);
-	}
-	else {
-		show_add_buddy(NULL, NULL, NULL, NULL);
-	}
-}
 
 static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) {
 	if (GTK_IS_IMAGE(widget)) {
@@ -592,9 +637,7 @@
 {
 	/* Buddies menu */
 	{ N_("/_Buddies"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Buddies/_Add A Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0,
-	  "<StockItem>", GTK_STOCK_ADD },
-	{ N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0,
+		{ N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0,
 	  "<StockItem>", GAIM_STOCK_IM },
 	{ N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, 
 	  "<StockItem>", GAIM_STOCK_CHAT },
@@ -606,19 +649,27 @@
 	{ N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0,
 	  "<StockItem>", GTK_STOCK_QUIT },
 
+	/* Edit menu */
+	{ N_("/_Edit"), NULL, NULL, 0, "<Branch>" },
+	{ N_("/Edit/_Show Offline Buddies"), NULL, edit_mode_cb, 0, "<CheckItem>"},
+	{ N_("/Edit/_Add a Buddy..."), NULL, gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, 
+	{ N_("/Edit/Add a _Group..."), NULL, show_add_group, 0, NULL},
+	{ "/Edit/sep", NULL, NULL, 0, "<Separator>" },
+	{ N_("/Edit/A_ccounts"), "<CTL>A", account_editor, 0, NULL },
+	{ N_("/Edit/Preferences"), "<CTL>P", show_prefs, 0,
+	  "<StockItem>", GTK_STOCK_PREFERENCES },
+	{ N_("/Edit/Pr_ivacy"), NULL, show_privacy_options, 0, NULL },
+	
+
 	/* Tools */ 
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>" },
 	{ N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" },
 	{ N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" },
-	{ "/Tools/sep1", NULL, NULL, 0, "<Separator>" },
-	{ N_("/Tools/A_ccounts"), "<CTL>A", account_editor, 0, NULL },
-	{ N_("/Tools/Preferences"), "<CTL>P", show_prefs, 0,
-	  "<StockItem>", GTK_STOCK_PREFERENCES },
+	{ N_("/Tools/sep1"), NULL, NULL, 0, "<Separator>" },
 	{ N_("/Tools/_File Transfers"), NULL, gaim_show_xfer_dialog, 0,
 	  "<StockItem>", GTK_STOCK_REVERT_TO_SAVED },
 	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>" },
 	{ N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" },
-	{ N_("/Tools/Pr_ivacy"), NULL, show_privacy_options, 0, NULL },
 	{ N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL },
 
 	/* Help */
@@ -680,12 +731,14 @@
 			       "%s %s"  /* Nickname */
 			       "%s %s"     /* Idle */
 			       "%s %s"     /* Warning */
+			       "%s"        /* Offline */
 			       "%s%s",    /* Status */
 			       b->name,
 			       aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "",
 			       nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "",
 			       b->idle ? _("\n<b>Idle:</b>") : "", b->idle ? idletime : "",
 			       b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "",
+			       !b->present ? _("\n<b>Status:</b> Offline") : "",
 			       statustext ? "\n" : "", statustext ? statustext : "");
 	if(warning)
 		g_free(warning);
@@ -715,11 +768,15 @@
 	int scalesize = 30;
 
 	struct prpl* prpl = find_prpl(b->account->protocol);
+
+	if (!prpl) 
+		return NULL;
+
 	if (prpl->list_icon)
 		protoname = prpl->list_icon(b->account, b);
 	if (prpl->list_emblems)
 		prpl->list_emblems(b, &se, &sw, &nw, &ne);
-
+	
 	if (size == GAIM_STATUS_ICON_SMALL) {
 		scalesize = 15;
 		sw = nw = ne = NULL; /* So that only the se icon will composite */
@@ -842,7 +899,7 @@
 
 
 	/* Idle grey buddies affects the whole row.  This converts the status icon to greyscale. */
-	if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS)
+	if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS) || gtkblist->editmode)
 		gdk_pixbuf_saturate_and_pixelate(scale, scale, 0, FALSE);
 	return scale;
 }
@@ -864,7 +921,7 @@
 
 
 	if (buf) {
-		if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) {
+		if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS) || gtkblist->editmode) {
 			gdk_pixbuf_saturate_and_pixelate(buf, buf, 0, FALSE);
 		}
 		ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR);
@@ -887,7 +944,7 @@
 	time_t t;
 
 	if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
-		if (b->idle > 0 && blist_options & OPT_BLIST_GREY_IDLERS && !selected) {
+		if ((b->idle > 0 && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || gtkblist->editmode) {
 			text =  g_strdup_printf("<span color='dim grey'>%s</span>",
 						esc);
 			g_free(esc);
@@ -901,7 +958,7 @@
 	ihrs = (t - b->idle) / 3600;
 	imin = ((t - b->idle) / 60) % 60;
 
-	if (prpl->status_text) {
+	if (prpl && prpl->status_text) {
 		char *tmp = prpl->status_text(b);
 		const char *end;
 
@@ -952,19 +1009,23 @@
 		warning = g_strdup_printf(_("Warned (%d%%) "), b->evil);
 
 	if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) {
-		text =  g_strdup_printf("<span color='dim grey'>%s</span>\n<span color='dim grey' size='smaller'>%s%s%s</span>",
+		text =  g_strdup_printf("<span color='dim grey'>%s</span>\n"
+					"<span color='dim grey' size='smaller'>%s%s%s%s</span>",
 					esc,
 					statustext != NULL ? statustext : "",
 					idletime != NULL ? idletime : "", 
-					warning != NULL ? warning : "");
-	} else if (statustext == NULL && idletime == NULL && warning == NULL) {
-		text = g_strdup_printf("%s", esc);
+					warning != NULL ? warning : "",
+					!b->present ? _("Offline ") : "");
+	} else if (statustext == NULL && idletime == NULL && warning == NULL && b->present) {
+		text = g_strdup(esc);
 	} else {
-		text = g_strdup_printf("%s\n<span %s size='smaller'>%s%s%s</span>", esc,
-				selected ? "" : "color='dim grey'",
-				statustext != NULL ? statustext :  "",
-				idletime != NULL ? idletime : "", 
-				warning != NULL ? warning : "");
+		text = g_strdup_printf("%s\n"
+				       "<span %s size='smaller'>%s%s%s%s</span>", esc,
+				       selected ? "" : "color='dim grey'",
+				       statustext != NULL ? statustext :  "",
+				       idletime != NULL ? idletime : "", 
+				       warning != NULL ? warning : "",
+				       !b->present ? _("Offline ") : "");
 	}
 	if (idletime)
 		g_free(idletime);
@@ -1097,6 +1158,7 @@
 						 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
 
 	gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
+	gtk_widget_set_size_request(gtkblist->treeview, -1, 200);
 
 	/* Set up selection stuff */
 
@@ -1195,8 +1257,8 @@
 	GaimBlistNode *buddy;
 
 	while (group) {
+		buddy = group->child;
 		gaim_gtk_blist_update(list, group);
-		buddy = group->child;
 		while (buddy) {
 			gaim_gtk_blist_update(list, buddy);
 			buddy = buddy->next;
@@ -1327,7 +1389,7 @@
 	if (!get_iter_from_node(node, &iter)) { /* This is a newly added node */
 		new_entry = TRUE;
 		if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
-			if (((struct buddy*)node)->present) {
+			if (((struct buddy*)node)->present || (gtkblist->editmode && ((struct buddy*)node)->account->gc)) {
 				GtkTreeIter groupiter;
 				GaimBlistNode *oldersibling;
 				GtkTreeIter oldersiblingiter;
@@ -1377,9 +1439,44 @@
 				}
 			}
 		}
+		else if (GAIM_BLIST_NODE_IS_GROUP(node) && gtkblist->editmode) {
+			GaimBlistNode *oldersibling;
+			GtkTreeIter oldersiblingiter;
+			GdkPixbuf *groupicon = gtk_widget_render_icon(gtkblist->treeview,
+								      GTK_STOCK_OPEN, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
+			char *esc = g_markup_escape_text(((struct group*)node)->name, -1);
+			char *mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
+			g_free(esc);
+			oldersibling = node->prev;
+			while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) {
+				oldersibling = oldersibling->prev;
+			}
+			
+			gtk_tree_store_insert_after(gtkblist->treemodel, &iter, NULL, oldersibling ? &oldersiblingiter : NULL);
+			gtk_tree_store_set(gtkblist->treemodel, &iter,
+					   STATUS_ICON_COLUMN, groupicon,
+					   NAME_COLUMN, mark,
+					   NODE_COLUMN, node,
+					   -1);
+			g_free(mark);
+			g_object_unref(groupicon);
+		}
+		
+	} else if (GAIM_BLIST_NODE_IS_GROUP(node)) {
+			if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(gtkblist->treemodel), &iter) == FALSE && gtkblist->editmode == FALSE)
+			gtk_tree_store_remove(gtkblist->treemodel, &iter);
+		else {
+			char *esc = g_markup_escape_text(((struct group*)node)->name, -1);
+			char *mark = g_strdup_printf("<span weight='bold'>%s</span>", esc);
+			g_free(esc);
+			gtk_tree_store_set(gtkblist->treemodel, &iter,
+					   NAME_COLUMN, mark,
+					   -1);
+			g_free(mark);
+		}
 	}
 
-	if (GAIM_BLIST_NODE_IS_BUDDY(node) && ((struct buddy*)node)->present) {
+	if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present || (gtkblist->editmode && ((struct buddy*)node)->account->gc))) {
 		GdkPixbuf *status, *avatar;
 		char *mark;
 		char *warning = NULL, *idle = NULL;
@@ -1456,8 +1553,9 @@
 			gaim_gtk_blist_update(list, afsad);
 			afsad = afsad->next;
 		}
+		
+		gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
 	}
-	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
 }
 
 static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list)
--- a/src/dialogs.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/dialogs.c	Mon Mar 31 07:19:46 2003 +0000
@@ -441,6 +441,18 @@
 	g_free(name);
 }
 
+void do_remove_group(struct group *g)
+{
+	GaimBlistNode *b = ((GaimBlistNode*)g)->child;
+	while (b) {
+		struct buddy *bd = (struct buddy *)b;
+		serv_remove_buddy(bd->account->gc, bd->name, g->name);
+		b = b->next;
+	}
+	gaim_blist_remove_group(g);
+	gaim_blist_save();
+}
+
 void show_confirm_del(struct gaim_connection *gc, gchar *name)
 {
 	struct buddy *bd = gaim_find_buddy(gc->account, name);
@@ -453,7 +465,13 @@
 	g_free(text);
 }
 
-
+void show_confirm_del_group(struct group *g)
+{
+     	char *text = g_strdup_printf(_("You are about to remove he group %s and all its members from your buddy list.  Do you want to continue?"), 
+			       g->name);
+	do_ask_dialog(_("Remove Group"), text, g, _("Remove Buddy"), do_remove_group, _("Cancel"), NULL, NULL, FALSE);
+	g_free(text);
+}
 
 /*------------------------------------------------------------------------*/
 /*  The dialog for getting an error                                       */
@@ -3922,8 +3940,7 @@
 					serv_rename_group(account->gc, g, new_name);
 					accts = g_slist_remove(accts, accts->data);
 				}
-				g_snprintf(g->name, sizeof(g->name), "%s", new_name);
-				gaim_blist_rename_group(g, prevname);
+				gaim_blist_rename_group(g, new_name);
 				g_free(prevname);
 			}
 			gaim_blist_save();
--- a/src/gtklist.h	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/gtklist.h	Mon Mar 31 07:19:46 2003 +0000
@@ -52,6 +52,8 @@
 	GtkWidget *treeview;            /**< It's a treeview... d'uh. */
 	GtkTreeStore *treemodel;        /**< This is the treemodel.  */
 		
+	gboolean *editmode;             /**< TRUE if offline buddies are to be shown in the list. */ 
+
 	GtkTreeViewColumn *idle_column, 
 		*warning_column, 
 		*buddy_icon_column;
--- a/src/protocols/gg/gg.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/gg/gg.c	Mon Mar 31 07:19:46 2003 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 5113 2003-03-16 00:01:49Z faceprint $
+ * $Id: gg.c 5250 2003-03-31 07:19:46Z seanegan $
  *
  * Copyright (C) 2001 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  * 
@@ -1247,22 +1247,25 @@
 {
 	return "gadu-gadu";
 }
-#if 0
-	guint status;
-	if (uc == UC_UNAVAILABLE)
-		return (char **)gg_sunred_xpm;
-	status = uc >> 5;
-	/* Drop all masks */
-	status &= ~(GG_STATUS_FRIENDS_MASK);
-	if (status == GG_STATUS_AVAIL)
-		return (char **)gg_sunyellow_xpm;
-	if (status == GG_STATUS_BUSY)
-		return (char **)gg_suncloud_xpm;
-	if (status == GG_STATUS_INVISIBLE)
-		return (char **)gg_sunwhitered_xpm;
-	return (char **)gg_sunyellow_xpm;
+
+static void agg_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	int status;
+	if (b->present == 0)
+		*se = "offline";
+	else if (b->uc == UC_UNAVAILABLE)
+		*se = "away";
+	else {
+		status = b->uc >> 5;
+		/* Drop all masks */
+		status &= ~(GG_STATUS_FRIENDS_MASK);
+		if (status == GG_STATUS_BUSY)
+			*se = "busy";
+		else if (status == GG_STATUS_INVISIBLE)
+			*se = "invisiible";
+	}
 }
-#endif
+
 
 static void agg_set_permit_deny_dummy(struct gaim_connection *gc)
 {
@@ -1283,6 +1286,7 @@
 	ret->options = 0;
 	ret->name = g_strdup("Gadu-Gadu");
 	ret->list_icon = agg_list_icon;
+	ret->list_emblems = agg_list_emblems;
 	ret->away_states = agg_away_states;
 	ret->actions = agg_actions;
 	ret->buddy_menu = agg_buddy_menu;
--- a/src/protocols/irc/irc.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/irc/irc.c	Mon Mar 31 07:19:46 2003 +0000
@@ -2433,6 +2433,12 @@
 	return "irc";
 }
 
+static void irc_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	if (b->present == 0)
+		*se = "offline";
+}
+
 static int 
 getlocalip(char *ip) /* Thanks, libfaim */
 {
@@ -2812,6 +2818,7 @@
 	ret->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL;
 	ret->name = g_strdup("IRC");
 	ret->list_icon = irc_list_icon;
+	ret->list_emblems = irc_list_emblems;
 	ret->login = irc_login;
 	ret->close = irc_close;
 	ret->send_im = irc_send_im;
--- a/src/protocols/jabber/jabber.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/jabber/jabber.c	Mon Mar 31 07:19:46 2003 +0000
@@ -2648,6 +2648,7 @@
 	xmlnode_free(x);
 }
 
+#if 0  /* Faceprint!  Look here! */
 /*
  * Remove a buddy item from the roster entirely
  */
@@ -2666,6 +2667,7 @@
 		xmlnode_free(x);
 	}
 }
+#endif
 
 /*
  * Unsubscribe a buddy from our presence
@@ -2776,22 +2778,31 @@
 {
 	return "jabber";
 }
-/*
-	switch (uc) {
-	case UC_AWAY:
-		return available_away_xpm;
-	case UC_CHAT:
-		return available_chat_xpm;
-	case UC_XA:
-		return available_xa_xpm;
-	case UC_DND:
-		return available_dnd_xpm;
-	case UC_ERROR:
-		return available_error_xpm;
-	default:
-		return available_xpm;
+
+static void jabber_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	if (b->present == 0) {
+		*se = "offline";
+	} else {
+		switch (b->uc) {
+		case UC_AWAY:
+			*se = "away";
+			break;
+		case UC_CHAT:
+			*se = "chat";
+			break;
+		case UC_XA:
+			*se = "extendedaway";
+			break;
+		case UC_DND:
+			*se = "dnd";
+			break;
+		case UC_ERROR:
+			*se = "error";
+			break;
+		}
 	}
-	}*/
+}
 
 static GList *jabber_chat_info(struct gaim_connection *gc)
 {
@@ -3284,6 +3295,12 @@
 			pbm->label = _("Temporarily Hide From");
 			pbm->callback = jabber_invisible_to_buddy;
 		}
+
+		pbm->gc = gc;
+		m = g_list_append(m, pbm);
+		pbm = g_new0(struct proto_buddy_menu, 1);
+		pbm->label = _("Cancel Presence Notification");
+		pbm->callback = jabber_unsubscribe_buddy_from_us;
 		pbm->gc = gc;
 		m = g_list_append(m, pbm);
 	}
@@ -3291,32 +3308,6 @@
 	return m;
 }
 
-/*
- * Jabber protocol-specific "edit buddy menu" item(s)
- */
-static GList *jabber_edit_buddy_menu(struct gaim_connection *gc, char *who) {
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Get Info");
-	pbm->callback = jabber_get_info;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Remove From Roster");
-	pbm->callback = jabber_remove_buddy_roster_item;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Cancel Presence Notification");
-	pbm->callback = jabber_unsubscribe_buddy_from_us;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	return m;
-}
-
 static GList *jabber_away_states(struct gaim_connection *gc) {
 	GList *m = NULL;
 
@@ -4219,12 +4210,12 @@
 	ret->options = OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_CHAT_TOPIC;
 	ret->name = g_strdup("Jabber");
 	ret->list_icon = jabber_list_icon;
+	ret->list_emblems = jabber_list_emblems;
 	ret->status_text = jabber_status_text;
 	ret->tooltip_text = jabber_tooltip_text;
 	ret->away_states = jabber_away_states;
 	ret->actions = jabber_actions;
 	ret->buddy_menu = jabber_buddy_menu;
-	ret->edit_buddy_menu = jabber_edit_buddy_menu;
 	ret->login = jabber_login;
 	ret->close = jabber_close;
 	ret->send_im = jabber_send_im;
--- a/src/protocols/msn/msn.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/msn/msn.c	Mon Mar 31 07:19:46 2003 +0000
@@ -1590,18 +1590,15 @@
 {
 	return "msn";
 }
-/*
-	if (uc == 0)
-		return msn_online_xpm;
-	
-	uc >>= 1;
-	
-	if (uc == 2 || uc == 6)
-		return msn_occ_xpm;
-	
-	return msn_away_xpm;
+
+static void msn_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	if (b->present == 0)
+		*se = "offline";
+	else if (b->uc >> 1 == 2 || b->uc >> 1 == 6)
+		*se = "occupied";
 }
-*/
+
 static char *msn_get_away_text(int s)
 {
 	switch (s) {
@@ -2004,6 +2001,7 @@
 	ret->options = OPT_PROTO_MAIL_CHECK;
 	ret->name = g_strdup("MSN");
 	ret->list_icon = msn_list_icon;
+	ret->list_emblems = msn_list_emblems;
 	ret->buddy_menu = msn_buddy_menu;
 	ret->login = msn_login;
 	ret->close = msn_close;
--- a/src/protocols/napster/napster.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/napster/napster.c	Mon Mar 31 07:19:46 2003 +0000
@@ -557,6 +557,12 @@
 	return "napster";
 }
 
+static void nap_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	if (b->present == 0)
+		*se = "offline";
+}
+
 static struct prpl *my_protocol = NULL;
 
 G_MODULE_EXPORT void napster_init(struct prpl *ret)
@@ -579,6 +585,7 @@
 	ret->protocol = PROTO_NAPSTER;
 	ret->name = g_strdup("Napster");
 	ret->list_icon = nap_list_icon;
+	ret->list_emblems = nap_list_emblems;
 	ret->login = nap_login;
 	ret->close = nap_close;
 	ret->send_im = nap_send_im;
--- a/src/protocols/oscar/oscar.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Mon Mar 31 07:19:46 2003 +0000
@@ -3015,7 +3015,7 @@
 	struct gaim_connection *gc = b->account->gc;
 	struct oscar_data *od = gc->proto_data;
 	struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, normalize(b->name));
-
+       
 	if (bi) {
 		gchar *yay;
 		char *caps = caps_string(bi->caps);
@@ -5147,6 +5147,9 @@
 	char *emblems[4] = {NULL,NULL,NULL,NULL};
 	int i = 0;
 
+	if (b->present == 0)
+		emblems[i++] = "offline";
+
 	if (b->name && (b->uc & 0xffff0000) && isdigit(b->name[0])) {
 /*		int uc = b->uc >> 16;
 		if (uc & AIM_ICQ_STATE_INVISIBLE)
@@ -5545,7 +5548,7 @@
 			m = g_list_append(m, pbm);
 		}
 
-		if (aim_sncmp(gc->username, who)) {
+		if ((aim_sncmp(gc->username, who)) && b->present) {
 			pbm = g_new0(struct proto_buddy_menu, 1);
 			pbm->label = _("Direct IM");
 			pbm->callback = oscar_ask_direct_im;
@@ -5557,7 +5560,6 @@
 			pbm->callback = oscar_ask_sendfile;
 			pbm->gc = gc;
 			m = g_list_append(m, pbm);
-
 #if 0
 			pbm = g_new0(struct proto_buddy_menu, 1);
 			pbm->label = _("Get File");
@@ -5566,36 +5568,20 @@
 			m = g_list_append(m, pbm);
 #endif
 		}
-	}
-
-	return m;
-}
-
-static GList *oscar_edit_buddy_menu(struct gaim_connection *gc, char *who)
-{
-	struct oscar_data *od = gc->proto_data;
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	if (od->icq) {
-		pbm = g_new0(struct proto_buddy_menu, 1);
-		pbm->label = _("Get Info");
-		pbm->callback = oscar_get_info;
-		pbm->gc = gc;
-		m = g_list_append(m, pbm);
-	}
-
-	if (od->sess->ssi.received_data) {
-		char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, who);
-		if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, who)) {
-			pbm = g_new0(struct proto_buddy_menu, 1);
-			pbm->label = _("Re-request Authorization");
-			pbm->callback = gaim_auth_sendrequest;
-			pbm->gc = gc;
-			m = g_list_append(m, pbm);
+		
+		if (od->sess->ssi.received_data) {
+			char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, who);
+			if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, who)) {
+				pbm = g_new0(struct proto_buddy_menu, 1);
+				pbm->label = _("Re-request Authorization");
+				pbm->callback = gaim_auth_sendrequest;
+				pbm->gc = gc;
+				m = g_list_append(m, pbm);
+			}
 		}
-	}
-
+		
+	}
+	
 	return m;
 }
 
@@ -5903,7 +5889,6 @@
 	ret->away_states = oscar_away_states;
 	ret->actions = oscar_actions;
 	ret->buddy_menu = oscar_buddy_menu;
-	ret->edit_buddy_menu = oscar_edit_buddy_menu;
 	ret->login = oscar_login;
 	ret->close = oscar_close;
 	ret->send_im = oscar_send_im;
--- a/src/protocols/toc/toc.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/toc/toc.c	Mon Mar 31 07:19:46 2003 +0000
@@ -1282,21 +1282,30 @@
 {
 	return "aim";
 }
-/*
-	if (uc & UC_UNAVAILABLE)
-		return (char **)away_icon_xpm;
-	if (uc & UC_AOL)
-		return (char **)aol_icon_xpm;
-	if (uc & UC_NORMAL)
-		return (char **)free_icon_xpm;
-	if (uc & UC_ADMIN)
-		return (char **)admin_icon_xpm;
-	if (uc & UC_UNCONFIRMED)
-		return (char **)dt_icon_xpm;
-	if (uc & UC_WIRELESS)
-		return (char **)wireless_icon_xpm;
-	return NULL;
-	}*/
+
+static void oscar_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	char *emblems[4] = {NULL,NULL,NULL,NULL};
+	int i = 0;
+
+	if (b->present == 0) {
+		*se = "offline";
+		return;
+	} else {
+		if (b->uc & UC_UNAVAILABLE)
+			emblems[i++] = "away";
+		if (b->uc & UC_AOL)
+			emblems[i++] = "aol";
+		if (b->uc & UC_ADMIN)
+			emblems[i++] = "admin";
+		if (b->uc & UC_WIRELESS)
+			emblems[i++] = "wireless";
+	}
+	*se = emblems[0];
+	*sw = emblems[1];
+	*nw = emblems[2];
+	*ne = emblems[3];
+}
 
 static GList *toc_buddy_menu(struct gaim_connection *gc, char *who)
 {
--- a/src/protocols/yahoo/yahoo.c	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/protocols/yahoo/yahoo.c	Mon Mar 31 07:19:46 2003 +0000
@@ -1015,15 +1015,27 @@
 {
 	return "yahoo";
 }
-/*
-  if ((uc >> 2) == YAHOO_STATUS_IDLE)
-  return status_idle_xpm;
-  else if (uc & UC_UNAVAILABLE)
-  return status_away_xpm;
-  else if (uc & YAHOO_STATUS_GAME)
-  return status_game_xpm;
-  return status_here_xpm;
-  }*/
+
+static void yahoo_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
+{
+	int i = 0;
+	char *emblems[4] = {NULL,NULL,NULL,NULL};
+	if (b->present == 0) {
+		*se = "offline";
+		return;
+	} else {
+		if ((b->uc >> 2) == YAHOO_STATUS_IDLE)
+			emblems[i++] = "idle";
+		if (b->uc & UC_UNAVAILABLE)
+			emblems[i++] = "away";
+		if (b->uc & YAHOO_STATUS_GAME)
+			emblems[i++] = "game";
+	}
+	*se = emblems[0];
+	*sw = emblems[1];
+	*nw = emblems[2];
+	*ne = emblems[3];
+}
 
 static char *yahoo_get_status_string(enum yahoo_status a)
 {
@@ -1380,6 +1392,7 @@
 	ret->close = yahoo_close;
 	ret->buddy_menu = yahoo_buddy_menu;
 	ret->list_icon = yahoo_list_icon;
+	ret->list_emblems = yahoo_list_emblems;
 	ret->status_text = yahoo_status_text;
 	ret->tooltip_text = yahoo_tooltip_text;
 	ret->actions = yahoo_actions;
--- a/src/prpl.h	Mon Mar 31 02:38:44 2003 +0000
+++ b/src/prpl.h	Mon Mar 31 07:19:46 2003 +0000
@@ -207,7 +207,6 @@
 	/* user_opts is a GList* of g_malloc'd struct proto_user_opts */
 	GList *user_opts;
 	GList *(* buddy_menu)(struct gaim_connection *, char *);
-	GList *(* edit_buddy_menu)(struct gaim_connection *, char *);
 	GList *(* chat_info)(struct gaim_connection *);
 
 	/* All the server-related functions */