changeset 14712:b15c2eaeb67f

[gaim-migrate @ 17466] Patch #1573404 from Richard Nelson (wabz): "This patch keeps the blist sorted (sorts rows at the current depth when the text for a given row changes). Adds a menu "Options" to the blist and adds the options to sort by status, alphabetically and toggle offline buddies." I changed some of the stuff from the patch. Hopefully I didn't break anything. I also added a "Sort by log size" option. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Thu, 12 Oct 2006 03:26:33 +0000
parents 556a112ab6ca
children 07f572e08773
files console/gntblist.c console/libgnt/gnttree.c console/libgnt/gnttree.h
diffstat 3 files changed, 237 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntblist.c	Thu Oct 12 02:51:52 2006 +0000
+++ b/console/gntblist.c	Thu Oct 12 03:26:33 2006 +0000
@@ -99,6 +99,11 @@
 static const char * get_display_name(GaimBlistNode *node);
 static void savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old);
 
+/* Sort functions */
+static int blist_node_compare_text(GaimBlistNode *n1, GaimBlistNode *n2);
+static int blist_node_compare_status(GaimBlistNode *n1, GaimBlistNode *n2);
+static int blist_node_compare_log(GaimBlistNode *n1, GaimBlistNode *n2);
+
 static gboolean
 is_contact_online(GaimContact *contact)
 {
@@ -188,6 +193,7 @@
 	if (node->ui_data != NULL) {
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
+		gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
 	}
 
 	if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
@@ -1375,6 +1381,17 @@
 	GaimBlistNode *node;
 	GaimBuddyList *list;
 
+	if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "text") == 0) {
+		gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
+			(GCompareFunc)blist_node_compare_text);
+	} else if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "status") == 0) {
+		gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
+			(GCompareFunc)blist_node_compare_status);
+	} else if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "log") == 0) {
+		gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
+			(GCompareFunc)blist_node_compare_log);
+	}
+	
 	list = gaim_get_blist();
 	node = gaim_blist_get_root();
 	while (node)
@@ -1475,11 +1492,14 @@
 	gaim_prefs_add_int(PREF_ROOT "/position/y", 0);
 	gaim_prefs_add_bool(PREF_ROOT "/idletime", TRUE);
 	gaim_prefs_add_bool(PREF_ROOT "/showoffline", FALSE);
+	gaim_prefs_add_string(PREF_ROOT "/sort_type", "text");
 
 	gg_blist_show();
 
 	gaim_prefs_connect_callback(gg_blist_get_handle(),
 			PREF_ROOT "/showoffline", redraw_blist, NULL);
+	gaim_prefs_connect_callback(gg_blist_get_handle(),
+			PREF_ROOT "/sort_type", redraw_blist, NULL);
 
 	return;
 }
@@ -1621,7 +1641,7 @@
 }
 
 static int
-blist_node_compare(GaimBlistNode *n1, GaimBlistNode *n2)
+blist_node_compare_text(GaimBlistNode *n1, GaimBlistNode *n2)
 {
 	const char *s1, *s2;
 	char *us1, *us2;
@@ -1640,7 +1660,6 @@
 			s2 = gaim_chat_get_name((GaimChat*)n2);
 			break;
 		case GAIM_BLIST_BUDDY_NODE:
-			/* XXX: reordering existing rows don't do well in GntTree */
 			return gaim_presence_compare(gaim_buddy_get_presence((GaimBuddy*)n1),
 					gaim_buddy_get_presence((GaimBuddy*)n2));
 			break;
@@ -1661,6 +1680,77 @@
 	return ret;
 }
 
+static int
+blist_node_compare_status(GaimBlistNode *n1, GaimBlistNode *n2)
+{
+	int ret;
+
+	g_return_val_if_fail(n1->type == n2->type, -1);
+
+	switch (n1->type) {
+		case GAIM_BLIST_CONTACT_NODE:
+			n1 = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)n1);
+			n2 = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)n2);
+			/* now compare the presence of the priority buddies */
+		case GAIM_BLIST_BUDDY_NODE:
+			ret = gaim_presence_compare(gaim_buddy_get_presence((GaimBuddy*)n1),
+					gaim_buddy_get_presence((GaimBuddy*)n2));
+			if (ret != 0)
+				return ret;
+			break;
+		default:
+			break;
+	}
+
+	/* Sort alphabetically if presence is not comparable */
+	ret = blist_node_compare_text(n1, n2);
+
+	return ret;
+}
+
+static int
+get_contact_log_size(GaimBlistNode *c)
+{
+	int log = 0;
+	GaimBlistNode *node;
+
+	for (node = c->child; node; node = node->next) {
+		GaimBuddy *b = (GaimBuddy*)node;
+		log += gaim_log_get_total_size(GAIM_LOG_IM, b->name, b->account);
+	}
+
+	return log;
+}
+
+static int
+blist_node_compare_log(GaimBlistNode *n1, GaimBlistNode *n2)
+{
+	int ret;
+	GaimBuddy *b1, *b2;
+
+	g_return_val_if_fail(n1->type == n2->type, -1);
+
+	switch (n1->type) {
+		case GAIM_BLIST_BUDDY_NODE:
+			b1 = (GaimBuddy*)n1;
+			b2 = (GaimBuddy*)n2;
+			ret = gaim_log_get_total_size(GAIM_LOG_IM, b2->name, b2->account) - 
+					gaim_log_get_total_size(GAIM_LOG_IM, b1->name, b1->account);
+			if (ret != 0)
+				return ret;
+			break;
+		case GAIM_BLIST_CONTACT_NODE:
+			ret = get_contact_log_size(n2) - get_contact_log_size(n1);
+			if (ret != 0)
+				return ret;
+			break;
+		default:
+			break;
+	}
+	ret = blist_node_compare_text(n1, n2);
+	return ret;
+}
+
 static gboolean
 blist_clicked(GntTree *tree, GntMouseEvent event, int x, int y, gpointer ggblist)
 {
@@ -1678,27 +1768,19 @@
 		action->callback(action);
 }
 
-static void
-reconstruct_accounts_menu()
+static GntMenuItem *reconstruct_accounts_menu()
 {
-	GntWidget *menu, *sub;
-	GntMenuItem *item;
-	GntWindow *window;
+	GntWidget *sub;
+	GntMenuItem *acc, *item;
 	GList *iter;
 
 	if (!ggblist)
-		return;
-
-	window  = GNT_WINDOW(ggblist->window);
+		return NULL;
 
-	menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
-	gnt_window_set_menu(window, GNT_MENU(menu));
-
-	item = gnt_menuitem_new(_("Accounts"));
-	gnt_menu_add_item(GNT_MENU(menu), item);
+	acc = gnt_menuitem_new(_("Accounts"));
 
 	sub = gnt_menu_new(GNT_MENU_POPUP);
-	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+	gnt_menuitem_set_submenu(acc, GNT_MENU(sub));
 
 	for (iter = gaim_accounts_get_all_active(); iter;
 			iter = g_list_delete_link(iter, iter)) {
@@ -1737,6 +1819,58 @@
 			}
 		}
 	}
+	return acc;
+}
+
+static void show_offline_cb(GntMenuItem *item, gpointer n)
+{
+	gaim_prefs_set_bool(PREF_ROOT "/showoffline",
+		!gaim_prefs_get_bool(PREF_ROOT "/showoffline"));
+}
+
+static void sort_blist_change_cb(GntMenuItem *item, gpointer n)
+{
+	gaim_prefs_set_string(PREF_ROOT "/sort_type", n);
+}
+
+static void
+create_menu()
+{
+	GntWidget *menu, *sub;
+	GntMenuItem *item;
+	GntWindow *window;
+
+	if (!ggblist)
+		return;
+
+	window = GNT_WINDOW(ggblist->window);
+	menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
+	gnt_window_set_menu(window, GNT_MENU(menu));
+
+	item = gnt_menuitem_new(_("Options"));
+	gnt_menu_add_item(GNT_MENU(menu), item);
+
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+
+	item = gnt_menuitem_new(_("Toggle offline buddies"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENUITEM(item), show_offline_cb, NULL);
+
+	item = gnt_menuitem_new(_("Sort by status"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "status");
+
+	item = gnt_menuitem_new(_("Sort alphabetically"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "text");
+
+	item = gnt_menuitem_new(_("Sort by log size"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "log");
+
+	item = reconstruct_accounts_menu();
+	gnt_menu_add_item(GNT_MENU(menu), item);
 }
 
 void gg_blist_show()
@@ -1755,7 +1889,7 @@
 	gnt_box_set_pad(GNT_BOX(ggblist->window), 0);
 
 	ggblist->tree = gnt_tree_new();
-	gnt_tree_set_compare_func(GNT_TREE(ggblist->tree), (GCompareFunc)blist_node_compare);
+
 	GNT_WIDGET_SET_FLAGS(ggblist->tree, GNT_WIDGET_NO_BORDER);
 	gnt_tree_set_col_width(GNT_TREE(ggblist->tree), 0, 25);
 	gnt_widget_set_size(ggblist->tree, gaim_prefs_get_int(PREF_ROOT "/size/width"),
@@ -1773,9 +1907,9 @@
 	gnt_widget_show(ggblist->window);
 
 	gaim_signal_connect(gaim_connections_get_handle(), "signed-on", gg_blist_get_handle(),
-				GAIM_CALLBACK(reconstruct_accounts_menu), NULL);
+				GAIM_CALLBACK(create_menu), NULL);
 	gaim_signal_connect(gaim_connections_get_handle(), "signed-off", gg_blist_get_handle(),
-				GAIM_CALLBACK(reconstruct_accounts_menu), NULL);
+				GAIM_CALLBACK(create_menu), NULL);
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_status_changed), ggblist);
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(),
@@ -1817,7 +1951,7 @@
 	g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed",
 				G_CALLBACK(status_text_changed), NULL);
 
-	reconstruct_accounts_menu(ggblist);
+	create_menu();
 
 	populate_buddylist();
 
--- a/console/libgnt/gnttree.c	Thu Oct 12 02:51:52 2006 +0000
+++ b/console/libgnt/gnttree.c	Thu Oct 12 03:26:33 2006 +0000
@@ -45,6 +45,18 @@
 static GntWidgetClass *parent_class = NULL;
 static guint signals[SIGS] = { 0 };
 
+/* Move the item at position old to position new */
+static GList *
+g_list_reposition_child(GList *list, int old, int new)
+{
+	gpointer item = g_list_nth_data(list, old);
+	list = g_list_remove(list, item);
+	if (old < new)
+		new--;   /* because the positions would have shifted after removing the item */
+	list = g_list_insert(list, item, new);
+	return list;
+}
+
 static GntTreeRow *
 _get_next(GntTreeRow *row, gboolean godeep)
 {
@@ -837,6 +849,75 @@
 	return NULL;
 }
 
+void gnt_tree_sort_row(GntTree *tree, gpointer key)
+{
+	GntTreeRow *row, *q, *s;
+	int current, newp;
+
+	if (!tree->compare)
+		return;
+
+	row = g_hash_table_lookup(tree->hash, key);
+	g_return_if_fail(row != NULL);
+
+	current = g_list_index(tree->list, key);
+
+	if (row->parent)
+		s = row->parent->child;
+	else
+		s = tree->root;
+
+	q = NULL;
+	while (s) {
+		if (tree->compare(row->key, s->key) < 0)
+			break;
+		q = s;
+		s = s->next;
+	}
+
+	/* Move row between q and s */
+	if (row == q || row == s)
+		return;
+
+	if (q == NULL) {
+		/* row becomes the first child of its parent */
+		row->prev->next = row->next;  /* row->prev cannot be NULL at this point */
+		if (row->next)
+			row->next->prev = row->prev;
+		if (row->parent)
+			row->parent->child = row;
+		else
+			tree->root = row;
+		row->next = s;
+		s->prev = row;  /* s cannot be NULL */
+		row->prev = NULL;
+		newp = g_list_index(tree->list, s) - 1;
+	} else {
+		if (row->prev) {
+			row->prev->next = row->next;
+		} else {
+			/* row was the first child of its parent */
+			if (row->parent)
+				row->parent->child = row->next;
+			else
+				tree->top = row->next;
+		}
+
+		if (row->next)
+			row->next->prev = row->prev;
+
+		q->next = row;
+		row->prev = q;
+		if (s)
+			s->prev = row;
+		row->next = s;
+		newp = g_list_index(tree->list, q) + 1;
+	}
+	tree->list = g_list_reposition_child(tree->list, current, newp);
+
+	redraw_tree(tree);
+}
+
 GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro)
 {
 	GntTreeRow *pr = NULL;
--- a/console/libgnt/gnttree.h	Thu Oct 12 02:51:52 2006 +0000
+++ b/console/libgnt/gnttree.h	Thu Oct 12 03:26:33 2006 +0000
@@ -123,6 +123,8 @@
 
 void gnt_tree_set_show_separator(GntTree *tree, gboolean set);
 
+void gnt_tree_sort_row(GntTree *tree, void *row);
+
 /* This will try to automatically adjust the width of the columns in the tree */
 void gnt_tree_adjust_columns(GntTree *tree);