changeset 13864:c7d84d4c5afa

[gaim-migrate @ 16328] Change the internals of GntTree. The change was required to accommodate expand/collapsing of the groups. I have added tooltips for Groups as well, which shows the online/total count. Do we like it? I have also added emblems at the beginning of the names of the buddies to indicate their status. Currently I am using ASCII-emblems ('o' for available, '.' for away, 'x' for offline (but I am not showing any offline buddies yet)), but I plan on using some cool unicode-emblems Sean suggested to me. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 24 Jun 2006 10:10:53 +0000
parents cf3eb9f311b2
children d78ab363e02d
files console/gntblist.c console/gntgaim.c console/libgnt/Makefile console/libgnt/gntbox.c console/libgnt/gnttree.c console/libgnt/gnttree.h console/libgnt/gntutils.c console/libgnt/gntutils.h
diffstat 8 files changed, 407 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntblist.c	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/gntblist.c	Sat Jun 24 10:10:53 2006 +0000
@@ -9,6 +9,7 @@
 #include "gnttree.h"
 
 #include "gntblist.h"
+#include <string.h>
 
 typedef struct
 {
@@ -23,6 +24,7 @@
 
 static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist);
 static void add_group(GaimGroup *group, GGBlist *ggblist);
+static void draw_tooltip(GGBlist *ggblist);
 
 static void
 new_node(GaimBlistNode *node)
@@ -54,6 +56,8 @@
 		GaimGroup *group = gaim_buddy_get_group((GaimBuddy*)node);
 		if (gaim_blist_get_group_online_count(group) == 0)
 			node_remove(list, (GaimBlistNode*)group);
+		else if (ggblist->tnode == (GaimBlistNode *)group)	/* Need to update the counts */
+			draw_tooltip(ggblist);
 	}
 
 	if (ggblist->tnode == node)
@@ -68,6 +72,10 @@
 	if (GAIM_BLIST_NODE_IS_BUDDY(node))
 	{
 		GaimBuddy *buddy = (GaimBuddy*)node;
+		if (gaim_presence_is_online(gaim_buddy_get_presence(buddy)))
+			add_buddy(buddy, list->ui_data);
+		else
+			node_remove(gaim_get_blist(), node);
 	}
 }
 
@@ -108,6 +116,52 @@
 			group->name, NULL, NULL);
 }
 
+static const char *
+get_buddy_display_name(GaimBuddy *buddy)
+{
+	static char text[2096];
+	char status[8];
+	GaimStatusPrimitive prim;
+	GaimPresence *presence;
+	GaimStatus *now;
+
+	presence = gaim_buddy_get_presence(buddy);
+	now = gaim_presence_get_active_status(presence);
+
+	prim = gaim_status_type_get_primitive(gaim_status_get_type(now));
+
+	switch(prim)
+	{
+#if 1
+		case GAIM_STATUS_OFFLINE:
+			strncpy(status, "x", sizeof(status) - 1);
+			break;
+		case GAIM_STATUS_AVAILABLE:
+			strncpy(status, "o", sizeof(status) - 1);
+			break;
+		default:
+			strncpy(status, ".", sizeof(status) - 1);
+			break;
+#else
+		/* XXX: Let's use these some time */
+		case GAIM_STATUS_OFFLINE:
+			strncpy(status, "⊗", sizeof(status) - 1);
+			break;
+		case GAIM_STATUS_AVAILABLE:
+			/* XXX: Detect idleness */
+			strncpy(status, "◯", sizeof(status) - 1);
+			break;
+		default:
+			strncpy(status, "⊖", sizeof(status) - 1);
+			break;
+#endif
+	}
+
+	snprintf(text, sizeof(text) - 1, "%s %s", status, gaim_buddy_get_alias(buddy));
+
+	return text;
+}
+
 static void
 add_buddy(GaimBuddy *buddy, GGBlist *ggblist)
 {
@@ -120,7 +174,10 @@
 	add_group(group, ggblist);
 
 	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
-				gaim_buddy_get_alias(buddy), group, NULL);
+				get_buddy_display_name(buddy), group, NULL);
+
+	if (ggblist->tnode == (GaimBlistNode*)group)
+		draw_tooltip(ggblist);
 }
 
 static void
@@ -147,17 +204,20 @@
 }
 
 static void
-selection_changed(GntWidget *widget, int old, int current, GGBlist *ggblist)
+draw_tooltip(GGBlist *ggblist)
 {
 	GaimBlistNode *node;
-	GntTree *tree = GNT_TREE(widget);
 	int x, y, top, width;
 	GString *str;
 	GaimPlugin *prpl;
 	GaimPluginProtocolInfo *prpl_info;
 	GaimAccount *account;
-	GntWidget *box, *label;
-	char *title;
+	GntTree *tree;
+	GntWidget *widget, *box, *label;
+	char *title = NULL;
+
+	widget = ggblist->tree;
+	tree = GNT_TREE(widget);
 
 	if (ggblist->tooltip)
 	{
@@ -195,6 +255,16 @@
 
 		title = g_strdup(gaim_buddy_get_name(buddy));
 	}
+	else if (GAIM_BLIST_NODE_IS_GROUP(node))
+	{
+		GaimGroup *group = (GaimGroup *)node;
+
+		g_string_append_printf(str, _("Online: %d\nTotal: %d"),
+						gaim_blist_get_group_online_count(group),
+						gaim_blist_get_group_size(group, FALSE));
+
+		title = g_strdup(group->name);
+	}
 	else
 	{
 		g_string_free(str, TRUE);
@@ -217,12 +287,20 @@
 
 	gnt_widget_set_position(box, x, y);
 	gnt_widget_draw(box);
-	
+
+	g_free(title);
 	g_string_free(str, TRUE);
 	ggblist->tooltip = box;
 	ggblist->tnode = node;
 }
 
+static void
+selection_changed(GntWidget *widget, gpointer old, gpointer current, GGBlist *ggblist)
+{
+	draw_tooltip(ggblist);
+}
+
+
 static gboolean
 key_pressed(GntWidget *widget, const char *text, GGBlist *ggblist)
 {
@@ -239,6 +317,14 @@
 	return FALSE;
 }
 
+static void
+buddy_status_changed(GaimBuddy *buddy, GaimStatus *old, GaimStatus *now, GGBlist *ggblist)
+{
+	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, get_buddy_display_name(buddy));
+	if (ggblist->tnode == (GaimBlistNode*)buddy)
+		draw_tooltip(ggblist);
+}
+
 void gg_blist_init()
 {
 	ggblist = g_new0(GGBlist, 1);
@@ -256,13 +342,16 @@
 
 	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
 	gnt_widget_show(ggblist->window);
+
+	gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(),
+				GAIM_CALLBACK(buddy_status_changed), ggblist);
 	
+#if 0
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_signed_on), ggblist);
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_signed_off), ggblist);
 
-#if 0
 	/* These I plan to use to indicate unread-messages etc. */
 	gaim_signal_connect(gaim_conversations_get_handle(), "received-im-msg", gg_blist_get_handle(),
 				GAIM_CALLBACK(received_im_msg), list);
--- a/console/gntgaim.c	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/gntgaim.c	Sat Jun 24 10:10:53 2006 +0000
@@ -168,6 +168,9 @@
 
 int main(int argc, char **argv)
 {
+	/* XXX: Don't puke */
+	freopen("/dev/null", "w", stderr);
+
 	/* Initialize the libgaim stuff */
 	init_libgaim();
 
--- a/console/libgnt/Makefile	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/Makefile	Sat Jun 24 10:10:53 2006 +0000
@@ -36,7 +36,7 @@
 
 all: libgnt
 
-test: $(OBJECTS)
+test2: $(OBJECTS)
 key: $(OBJECTS)
 
 gntwidget.o: gntwidget.c $(HEADERS)
--- a/console/libgnt/gntbox.c	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/gntbox.c	Sat Jun 24 10:10:53 2006 +0000
@@ -32,7 +32,7 @@
 		else
 		{
 			/* XXX: Position of the title might be configurable */
-			pos = (widget->priv.width - pos - 2) / 2;
+			pos = (widget->priv.width - pos) / 2;
 		}
 		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TITLE));
 		mvwprintw(widget->window, 0, pos, title);
--- a/console/libgnt/gnttree.c	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/gnttree.c	Sat Jun 24 10:10:53 2006 +0000
@@ -1,6 +1,8 @@
 #include "gnttree.h"
 #include "gntutils.h"
 
+#include <string.h>
+
 enum
 {
 	SIG_SELECTION_CHANGED,
@@ -17,7 +19,8 @@
 	char *text;
 	void *data;		/* XXX: unused */
 
-	/* XXX: These are also unused */
+	gboolean collapsed;
+
 	GntTreeRow *parent;
 	GntTreeRow *child;
 	GntTreeRow *next;
@@ -27,29 +30,165 @@
 static GntWidgetClass *parent_class = NULL;
 static guint signals[SIGS] = { 0 };
 
+static GntTreeRow *
+_get_next(GntTreeRow *row, gboolean godeep)
+{
+	if (row == NULL)
+		return NULL;
+	if (godeep && row->child)
+		return row->child;
+	if (row->next)
+		return row->next;
+	return _get_next(row->parent, FALSE);
+}
+
+static GntTreeRow *
+get_next(GntTreeRow *row)
+{
+	if (row == NULL)
+		return;
+	return _get_next(row, !row->collapsed);
+}
+
+/* Returns the n-th next row. If it doesn't exist, returns NULL */
+static GntTreeRow *
+get_next_n(GntTreeRow *row, int n)
+{
+	while (row && n--)
+		row = get_next(row);
+	return row;
+}
+
+/* Returns the n-th next row. If it doesn't exist, then the last non-NULL node */
+static GntTreeRow *
+get_next_n_opt(GntTreeRow *row, int n, int *pos)
+{
+	GntTreeRow *next = row;
+	int r = 0;
+
+	if (row == NULL)
+		return NULL;
+
+	while (row && n--)
+	{
+		row = get_next(row);
+		if (row)
+		{
+			next = row;
+			r++;
+		}
+	}
+
+	if (pos)
+		*pos = r;
+
+	return next;
+}
+
+static GntTreeRow *
+get_last_child(GntTreeRow *row)
+{
+	if (row == NULL)
+		return NULL;
+	if (!row->collapsed && row->child)
+		row = row->child;
+	else
+		return row;
+
+	while(row->next)
+		row = row->next;
+	if (row->child)
+		row = get_last_child(row->child);
+	return row;
+}
+
+static GntTreeRow *
+get_prev(GntTreeRow *row)
+{
+	if (row == NULL)
+		return NULL;
+	if (row->prev)
+		return get_last_child(row->prev);
+	return row->parent;
+}
+
+static GntTreeRow *
+get_prev_n(GntTreeRow *row, int n)
+{
+	while (row && n--)
+		row = get_prev(row);
+	return row;
+}
+
+/* Distance of row from the root */
+/* XXX: This is uber-inefficient */
+static int
+get_root_distance(GntTreeRow *row)
+{
+	if (row == NULL)
+		return -1;
+	return get_root_distance(get_prev(row)) + 1;
+}
+
+/* Returns the distance between a and b. 
+ * If a is 'above' b, then the distance is positive */
+static int
+get_distance(GntTreeRow *a, GntTreeRow *b)
+{
+	/* First get the distance from a to the root.
+	 * Then the distance from b to the root.
+	 * Subtract.
+	 * It's not that good, but it works. */
+	int ha = get_root_distance(a);
+	int hb = get_root_distance(b);
+
+	return (hb - ha);
+}
+
 static void
 redraw_tree(GntTree *tree)
 {
 	int start;
 	GntWidget *widget = GNT_WIDGET(tree);
-	GList *iter;
+	GntTreeRow *row;
 	int pos;
+	gboolean deep;
 
 	if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
 		pos = 0;
 	else
 		pos = 1;
 
+	if (tree->top == NULL)
+		tree->top = tree->root;
+	if (tree->current == NULL)
+		tree->current = tree->root;
+
 	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
 
-	for (start = tree->top, iter = g_list_nth(tree->list, tree->top);
-				iter && start < tree->bottom; start++, iter = iter->next)
+	deep = TRUE;
+	row = tree->top;
+	for (start = pos; row && start < widget->priv.height - pos;
+				start++, row = get_next(row))
 	{
-		char str[2096];	/* XXX: This should be safe for any terminal */
+		char str[2048];
 		int wr;
-		GntTreeRow *row = g_hash_table_lookup(tree->hash, iter->data);
+		char format[16] = "";
+
+		deep = TRUE;
 
-		if ((wr = snprintf(str, widget->priv.width, "%s", row->text)) >= widget->priv.width)
+		if (row->parent == NULL && row->child)
+		{
+			if (row->collapsed)
+			{
+				strcpy(format, "+ ");
+				deep = FALSE;
+			}
+			else
+				strcpy(format, "- ");
+		}
+
+		if ((wr = g_snprintf(str, widget->priv.width, "%s%s", format, row->text)) >= widget->priv.width)
 		{
 			/* XXX: ellipsize */
 			str[widget->priv.width - 1 - pos] = 0;
@@ -61,19 +200,20 @@
 			str[wr] = 0;
 		}
 		
-		if (start == tree->current)
+		if (row == tree->current)
 		{
 			wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
-			mvwprintw(widget->window, start - tree->top + pos, pos, str);
+			mvwprintw(widget->window, start, pos, str);
 			wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
 		}
 		else
-			mvwprintw(widget->window, start - tree->top + pos, pos, str);
+			mvwprintw(widget->window, start, pos, str);
+		tree->bottom = row;
 	}
 
-	while (start < tree->bottom)
+	while (start < widget->priv.height - pos)
 	{
-		mvwhline(widget->window, start - tree->top + pos, pos, ' ',
+		mvwhline(widget->window, start, pos, ' ',
 				widget->priv.width - pos * 2);
 		start++;
 	}
@@ -85,13 +225,12 @@
 gnt_tree_draw(GntWidget *widget)
 {
 	GntTree *tree = GNT_TREE(widget);
+	int bottom;
 
 	scrollok(widget->window, TRUE);
 	wsetscrreg(widget->window, 0, widget->priv.height - 1);
 
-	tree->top = 0;
-	tree->bottom = widget->priv.height -
-			(GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER) ? 0 : 2);
+	tree->top = tree->root;
 
 	redraw_tree(tree);
 	
@@ -116,32 +255,42 @@
 }
 
 static void
-tree_selection_changed(GntTree *tree, int old, int current)
+tree_selection_changed(GntTree *tree, GntTreeRow *old, GntTreeRow *current)
 {
 	g_signal_emit(tree, signals[SIG_SELECTION_CHANGED], 0, old, current);
 }
 
+static GntTreeRow *
+get_nth_row(GntTree *tree, int n)
+{
+	gpointer key = g_list_nth_data(tree->list, n);
+	return g_hash_table_lookup(tree->hash, key);
+}
+
 static gboolean
 gnt_tree_key_pressed(GntWidget *widget, const char *text)
 {
 	GntTree *tree = GNT_TREE(widget);
-	int old = tree->current;
+	GntTreeRow *old = tree->current;
+	GntTreeRow *row;
 
 	if (text[0] == 27)
 	{
-		if (strcmp(text+1, GNT_KEY_DOWN) == 0 && tree->current < g_list_length(tree->list) - 1)
+		int dist;
+		if (strcmp(text+1, GNT_KEY_DOWN) == 0 && (row = get_next(tree->current)) != NULL)
 		{
-			tree->current++;
-			if (tree->current >= tree->bottom)
-				gnt_tree_scroll(tree, 1 + tree->current - tree->bottom);
+			tree->current = row;
+			if ((dist = get_distance(tree->current, tree->bottom)) < 0)
+				gnt_tree_scroll(tree, -dist);
 			else
 				redraw_tree(tree);
 		}
-		else if (strcmp(text+1, GNT_KEY_UP) == 0 && tree->current > 0)
+		else if (strcmp(text+1, GNT_KEY_UP) == 0 && (row = get_prev(tree->current)) != NULL)
 		{
-			tree->current--;
-			if (tree->current < tree->top)
-				gnt_tree_scroll(tree, tree->current - tree->top);
+			tree->current = row;
+
+			if ((dist = get_distance(tree->current, tree->top)) > 0)
+				gnt_tree_scroll(tree, -dist);
 			else
 				redraw_tree(tree);
 		}
@@ -150,6 +299,16 @@
 	{
 		gnt_widget_activate(widget);
 	}
+	else if (text[0] == ' ' && text[1] == 0)
+	{
+		/* Space pressed */
+		GntTreeRow *row = tree->current;
+		if (row && row->child)
+		{
+			row->collapsed = !row->collapsed;
+			redraw_tree(tree);
+		}
+	}
 
 	if (old != tree->current)
 		tree_selection_changed(tree, old, tree->current);
@@ -184,8 +343,8 @@
 					 G_SIGNAL_RUN_LAST,
 					 G_STRUCT_OFFSET(GntTreeClass, selection_changed),
 					 NULL, NULL,
-					 gnt_closure_marshal_VOID__INT_INT,
-					 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+					 gnt_closure_marshal_VOID__POINTER_POINTER,
+					 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
 
 	DEBUG;
 }
@@ -268,16 +427,22 @@
 
 void gnt_tree_scroll(GntTree *tree, int count)
 {
-	if (tree->top == 0 && count < 0)
-		return;
+	GntTreeRow *row;
 
-	if (count > 0 && tree->bottom + count >= g_list_length(tree->list))
-		count = g_list_length(tree->list) - tree->bottom;
-	else if (count < 0 && tree->top + count < 0)
-		count = -tree->top;
-
-	tree->top += count;
-	tree->bottom += count;
+	if (count < 0)
+	{
+		if (get_root_distance(tree->top) == 0)
+			return;
+		row = get_prev_n(tree->top, -count);
+		if (row == NULL)
+			row = tree->root;
+		tree->top = row;
+	}
+	else
+	{
+		get_next_n_opt(tree->bottom, count, &count);
+		tree->top = get_next_n(tree->top, count);
+	}
 
 	redraw_tree(tree);
 }
@@ -300,7 +465,7 @@
 {
 	GntTreeRow *row = g_new0(GntTreeRow, 1), *pr = NULL;
 
-	g_hash_table_insert(tree->hash, key, row);
+	g_hash_table_replace(tree->hash, key, row);
 
 	if (tree->root == NULL)
 	{
@@ -342,11 +507,13 @@
 
 		if (pr == NULL)
 		{
-			if (tree->root)	tree->root->prev = row;
-			row->next = tree->root;
-			tree->root = row;
+			GntTreeRow *r = tree->root;
+			while (r->next)
+				r = r->next;
+			r->next = row;
+			row->prev = r;
 
-			tree->list = g_list_prepend(tree->list, key);
+			tree->list = g_list_append(tree->list, key);
 		}
 		else
 		{
@@ -366,12 +533,9 @@
 
 gpointer gnt_tree_get_selection_data(GntTree *tree)
 {
-	return g_list_nth_data(tree->list, tree->current);
-}
-
-int gnt_tree_get_selection_index(GntTree *tree)
-{
-	return tree->current;
+	if (tree->current)
+		return tree->current->key;	/* XXX: perhaps we should just get rid of 'data' */
+	return NULL;
 }
 
 /* XXX: Should this also remove all the children of the row being removed? */
@@ -380,24 +544,70 @@
 	GntTreeRow *row = g_hash_table_lookup(tree->hash, key);
 	if (row)
 	{
-		int len, pos;
+		gboolean redraw = FALSE;
+
+		if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) >= 0)
+			redraw = TRUE;
 
-		pos = g_list_index(tree->list, key);
+		/* Update root/top/current/bottom if necessary */
+		if (tree->root == row)
+			tree->root = get_next(row);
+		if (tree->top == row)
+		{
+			if (tree->top != tree->root)
+				tree->top = get_prev(row);
+			else
+				tree->top = get_next(row);
+			if (tree->current == row)
+				tree->current = tree->top;
+		}
+		else if (tree->current == row)
+		{
+			if (tree->current != tree->root)
+				tree->current = get_prev(row);
+			else
+				tree->current = get_next(row);
+		}
+		else if (tree->bottom == row)
+		{
+			tree->bottom = get_prev(row);
+		}
 
+		/* Fix the links */
+		if (row->next)
+			row->next->prev = row->prev;
+		if (row->parent && row->parent->child == row)
+			row->parent->child = row->next;
+		if (row->prev)
+			row->prev->next = row->next;
+		
 		g_hash_table_remove(tree->hash, key);
 		tree->list = g_list_remove(tree->list, key);
 
-		if (pos >= tree->top && pos < tree->bottom)
+		if (redraw)
 		{
 			redraw_tree(tree);
 		}
-		g_hash_table_replace(tree->hash, key, NULL);
 	}
 }
 
 int gnt_tree_get_selection_visible_line(GntTree *tree)
 {
-	return (tree->current - tree->top) +
+	return get_distance(tree->top, tree->current) +
 			!!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER));
 }
 
+void gnt_tree_change_text(GntTree *tree, gpointer key, const char *text)
+{
+	GntTreeRow *row = g_hash_table_lookup(tree->hash, key);
+	if (row)
+	{
+		g_free(row->text);
+		row->text = g_strdup_printf("%*s%s", TAB_SIZE * find_depth(row), "", text);
+
+		if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) > 0)
+			redraw_tree(tree);
+	}
+}
+
+
--- a/console/libgnt/gnttree.h	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/gnttree.h	Sat Jun 24 10:10:53 2006 +0000
@@ -27,10 +27,10 @@
 {
 	GntWidget parent;
 
-	int current;	/* current selection */
+	GntTreeRow *current;	/* current selection */
 
-	int top;		/* The index in 'list' of the topmost visible item */
-	int bottom;		/* The index in 'list' of the bottommost visible item */
+	GntTreeRow *top;		/* The topmost visible item */
+	GntTreeRow *bottom;		/* The bottommost visible item */
 	
 	GntTreeRow *root; /* The root of all evil */
 	
@@ -66,13 +66,13 @@
 
 gpointer gnt_tree_get_selection_data(GntTree *tree);
 
-int gnt_tree_get_selection_index(GntTree *tree);
-
 void gnt_tree_remove(GntTree *tree, gpointer key);
 
 /* Returns the visible line number of the selected row */
 int gnt_tree_get_selection_visible_line(GntTree *tree);
 
+void gnt_tree_change_text(GntTree *tree, gpointer key, const char *text);
+
 G_END_DECLS
 
 #endif /* GNT_TREE_H */
--- a/console/libgnt/gntutils.c	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/gntutils.c	Sat Jun 24 10:10:53 2006 +0000
@@ -98,3 +98,35 @@
 			data2);
 }
 
+void gnt_closure_marshal_VOID__POINTER_POINTER(GClosure *closure,
+										GValue *ret_value,
+										guint n_param_values,
+										const GValue *param_values,
+										gpointer invocation_hint,
+										gpointer marshal_data)
+{
+	typedef void (*func) (gpointer data1, gpointer, gpointer, gpointer data2);
+	register func callback;
+	register GCClosure *cc = (GCClosure*)closure;
+	register gpointer data1, data2;
+
+	g_return_if_fail(n_param_values == 3);
+
+	if (G_CCLOSURE_SWAP_DATA(closure))
+	{
+		data1 = closure->data;
+		data2 = g_value_peek_pointer(param_values + 0);
+	}
+	else
+	{
+		data1 = g_value_peek_pointer(param_values + 0);
+		data2 = closure->data;
+	}
+
+	callback = (func) (marshal_data ? marshal_data : cc->callback);
+	callback(data1,
+			g_value_get_pointer(param_values + 1) ,
+			g_value_get_pointer(param_values + 2) ,
+			data2);
+}
+
--- a/console/libgnt/gntutils.h	Sat Jun 24 08:54:33 2006 +0000
+++ b/console/libgnt/gntutils.h	Sat Jun 24 10:10:53 2006 +0000
@@ -21,3 +21,10 @@
 										gpointer invocation_hint,
 										gpointer marshal_data);
 
+void gnt_closure_marshal_VOID__POINTER_POINTER(GClosure *closure,
+										GValue *ret_value,
+										guint n_param_values,
+										const GValue *param_values,
+										gpointer invocation_hint,
+										gpointer marshal_data);
+