changeset 13964:0a0d2a1fd2bc

[gaim-migrate @ 16520] Add multi-column support for GntTree. Use it for email-notifications. Restore colors before exiting. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Wed, 19 Jul 2006 07:12:59 +0000
parents f7cfaee79982
children df0dba522147
files console/gntaccount.c console/gntblist.c console/gntnotify.c console/libgnt/gntcolors.c console/libgnt/gntcolors.h console/libgnt/gntcombobox.c console/libgnt/gntlabel.c console/libgnt/gntlabel.h console/libgnt/gntline.h console/libgnt/gntmain.c console/libgnt/gnttextview.h console/libgnt/gnttree.c console/libgnt/gnttree.h console/libgnt/test/focus.c console/libgnt/test/multiwin.c console/libgnt/test/wm.c
diffstat 16 files changed, 358 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntaccount.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/gntaccount.c	Wed Jul 19 07:12:59 2006 +0000
@@ -210,25 +210,26 @@
 
 	gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
 
-	accounts.tree = gnt_tree_new();
+	accounts.tree = gnt_tree_new_with_columns(2);
 	GNT_WIDGET_SET_FLAGS(accounts.tree, GNT_WIDGET_NO_BORDER);
 
 	for (iter = gaim_accounts_get_all(); iter; iter = iter->next)
 	{
 		GaimAccount *account = iter->data;
-		char *str = g_strdup_printf("%s (%s)",
-				gaim_account_get_username(account), gaim_account_get_protocol_id(account));
 
 		gnt_tree_add_choice(GNT_TREE(accounts.tree), account,
-				str, NULL, NULL);
+				gnt_tree_create_row(GNT_TREE(accounts.tree),
+					gaim_account_get_username(account),
+					gaim_account_get_protocol_name(account)),
+				NULL, NULL);
 		gnt_tree_set_choice(GNT_TREE(accounts.tree), account,
 				gaim_account_get_enabled(account, GAIM_GNT_UI));
-		g_free(str);
 	}
 
 	g_signal_connect(G_OBJECT(accounts.tree), "toggled", G_CALLBACK(account_toggled), NULL);
 	
-	gnt_widget_set_size(accounts.tree, 40, 10);
+	gnt_tree_set_col_width(GNT_TREE(accounts.tree), 0, 40);
+	gnt_tree_set_col_width(GNT_TREE(accounts.tree), 1, 10);
 	gnt_box_add_widget(GNT_BOX(accounts.window), accounts.tree);
 
 	gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
--- a/console/gntblist.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/gntblist.c	Wed Jul 19 07:12:59 2006 +0000
@@ -129,8 +129,9 @@
 	GaimBlistNode *node = (GaimBlistNode *)group;
 	if (node->ui_data)
 		return;
+	gnt_tree_remove(GNT_TREE(ggblist->tree), group);
 	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
-			group->name, NULL, NULL);
+			gnt_tree_create_row(GNT_TREE(ggblist->tree), group->name), NULL, NULL);
 }
 
 static const char *
@@ -191,8 +192,10 @@
 	group = gaim_chat_get_group(chat);
 	add_node((GaimBlistNode*)group, ggblist);
 
+	gnt_tree_remove(GNT_TREE(ggblist->tree), chat);
 	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
-				get_display_name(node), group, NULL);
+				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
+				group, NULL);
 }
 
 static void
@@ -206,8 +209,10 @@
 	group = gaim_buddy_get_group(buddy);
 	add_node((GaimBlistNode*)group, ggblist);
 
+	gnt_tree_remove(GNT_TREE(ggblist->tree), buddy);
 	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
-				get_display_name(node), group, NULL);
+				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
+				group, NULL);
 	if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy)))
 		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, GNT_TEXT_FLAG_DIM);
 	else
@@ -270,7 +275,8 @@
 {
 	GList *list;
 	
-	gnt_tree_add_row_after(tree, action, action->label, parent, NULL);
+	gnt_tree_add_row_after(tree, action,
+			gnt_tree_create_row(tree, action->label), parent, NULL);
 	for (list = action->children; list; list = list->next)
 		gnt_append_menu_action(tree, list->data, action);
 }
@@ -640,7 +646,7 @@
 static void
 update_buddy_display(GaimBuddy *buddy, GGBlist *ggblist)
 {
-	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, get_display_name((GaimBlistNode*)buddy));
+	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((GaimBlistNode*)buddy));
 	if (ggblist->tnode == (GaimBlistNode*)buddy)
 		draw_tooltip(ggblist);
 
@@ -685,7 +691,8 @@
 
 	ggblist->tree = gnt_tree_new();
 	GNT_WIDGET_SET_FLAGS(ggblist->tree, GNT_WIDGET_NO_BORDER);
-	gnt_widget_set_size(ggblist->tree, 25, getmaxy(stdscr) - 4);
+	gnt_tree_set_col_width(GNT_TREE(ggblist->tree), 0, 25);
+	gnt_widget_set_size(ggblist->tree, 0, getmaxy(stdscr) - 4);
 
 	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
 	gnt_widget_show(ggblist->window);
--- a/console/gntnotify.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/gntnotify.c	Wed Jul 19 07:12:59 2006 +0000
@@ -2,12 +2,19 @@
 #include <gntbox.h>
 #include <gntbutton.h>
 #include <gntlabel.h>
+#include <gnttree.h>
 
 #include <util.h>
 
 #include "gntnotify.h"
 #include "gntgaim.h"
 
+static struct
+{
+	GntWidget *window;
+	GntWidget *tree;
+} emaildialog;
+
 static void *
 gg_notify_message(GaimNotifyMsgType type, const char *title,
 		const char *primary, const char *secondary)
@@ -72,6 +79,44 @@
 	return ret;
 }
 
+static void
+reset_email_dialog()
+{
+	emaildialog.window = NULL;
+	emaildialog.tree = NULL;
+}
+
+static void
+setup_email_dialog()
+{
+	GntWidget *box, *tree, *button;
+	if (emaildialog.window)
+		return;
+
+	emaildialog.window = box = gnt_vbox_new(FALSE);
+	gnt_box_set_toplevel(GNT_BOX(box), TRUE);
+	gnt_box_set_title(GNT_BOX(box), _("Emails"));
+	gnt_box_set_fill(GNT_BOX(box), FALSE);
+	gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
+	gnt_box_set_pad(GNT_BOX(box), 0);
+
+	gnt_box_add_widget(GNT_BOX(box),
+			gnt_label_new_with_format(_("You have mail!"), GNT_TEXT_FLAG_BOLD));
+
+	emaildialog.tree = tree = gnt_tree_new_with_columns(3);
+	gnt_tree_set_col_width(GNT_TREE(tree), 0, 15);
+	gnt_tree_set_col_width(GNT_TREE(tree), 1, 25);
+	gnt_tree_set_col_width(GNT_TREE(tree), 2, 25);
+
+	gnt_box_add_widget(GNT_BOX(box), tree);
+
+	button = gnt_button_new(_("Close"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), box);
+	g_signal_connect(G_OBJECT(box), "destroy", G_CALLBACK(reset_email_dialog), NULL);
+}
+
 static void *
 gg_notify_emails(GaimConnection *gc, size_t count, gboolean detailed,
 		const char **subjects, const char **froms, const char **tos,
@@ -92,14 +137,16 @@
 	}
 	else
 	{
-		/* XXX: Yes, yes. I know, the combined dialog thing. Maybe later. */
-		g_string_append_printf(message,
-				_("You have received a mail \"%s\""), *subjects);
-		if (froms && *froms && **froms)
-			g_string_append_printf(message, _("\nfrom %s"), *froms);
-		g_string_append_printf(message, _(" to %s (%s)"),
-				tos ? *tos : gaim_account_get_username(account),
-				gaim_account_get_protocol_name(account));
+		setup_email_dialog();
+
+		gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(time(NULL)),
+				gnt_tree_create_row(GNT_TREE(emaildialog.tree),
+					tos ? *tos : gaim_account_get_username(account),
+					froms ? *froms : "[Unknown sender]",
+					*subjects),
+				NULL, NULL);
+		gnt_widget_show(emaildialog.window);
+		return NULL;
 	}
 
 	ret = gg_notify_message(GAIM_NOTIFY_MSG_INFO, _("New Mail"), _("You have mail!"), message->str);
--- a/console/libgnt/gntcolors.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntcolors.c	Wed Jul 19 07:12:59 2006 +0000
@@ -1,10 +1,40 @@
 #include <ncursesw/ncurses.h>
 #include "gntcolors.h"
 
+static struct
+{
+	short r, g, b;
+} colors[GNT_TOTAL_COLORS];
+
+static void
+backup_colors()
+{
+	short i;
+	for (i = 0; i < GNT_TOTAL_COLORS; i++)
+	{
+		color_content(i, &colors[i].r,
+				&colors[i].g, &colors[i].b);
+	}
+}
+
+static void
+restore_colors()
+{
+	short i;
+	for (i = 0; i < GNT_TOTAL_COLORS; i++)
+	{
+		init_color(i, colors[i].r,
+				colors[i].g, colors[i].b);
+	}
+}
+
 void gnt_init_colors()
 {
+	start_color();
 	if (can_change_color())
 	{
+		backup_colors();
+
 		/* XXX: Do some init_color()s */
 		init_color(GNT_COLOR_BLACK, 0, 0, 0);
 		init_color(GNT_COLOR_RED, 1000, 0, 0);
@@ -39,3 +69,9 @@
 	}
 }
 
+void
+gnt_uninit_colors()
+{
+	restore_colors();
+}
+
--- a/console/libgnt/gntcolors.h	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntcolors.h	Wed Jul 19 07:12:59 2006 +0000
@@ -32,4 +32,6 @@
 /* populate some default colors */
 void gnt_init_colors();
 
+void gnt_uninit_colors();
+
 #endif
--- a/console/libgnt/gntcombobox.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntcombobox.c	Wed Jul 19 07:12:59 2006 +0000
@@ -235,7 +235,8 @@
 
 void gnt_combo_box_add_data(GntComboBox *box, gpointer key, const char *text)
 {
-	gnt_tree_add_row_after(GNT_TREE(box->dropdown), key, text, NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(box->dropdown), key,
+			gnt_tree_create_row(GNT_TREE(box->dropdown), text), NULL, NULL);
 	if (box->selected == NULL)
 		set_selection(box, key);
 }
--- a/console/libgnt/gntlabel.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntlabel.c	Wed Jul 19 07:12:59 2006 +0000
@@ -126,3 +126,16 @@
 	return widget;
 }
 
+void gnt_label_set_text(GntLabel *label, const char *text)
+{
+	g_free(label->text);
+	label->text = g_strdup(text);
+
+	if (GNT_WIDGET(label)->window)
+	{
+		gnt_widget_hide(GNT_WIDGET(label));
+		gnt_label_size_request(GNT_WIDGET(label));
+		gnt_widget_draw(GNT_WIDGET(label));
+	}
+}
+
--- a/console/libgnt/gntlabel.h	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntlabel.h	Wed Jul 19 07:12:59 2006 +0000
@@ -46,6 +46,8 @@
 
 GntWidget *gnt_label_new_with_format(const char *text, GntTextFormatFlags flags);
 
+void gnt_label_set_text(GntLabel *label, const char *text);
+
 G_END_DECLS
 
 #endif /* GNT_LABEL_H */
--- a/console/libgnt/gntline.h	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntline.h	Wed Jul 19 07:12:59 2006 +0000
@@ -42,6 +42,9 @@
 
 GType gnt_line_get_gtype(void);
 
+#define gnt_hline_new() gnt_line_new(FALSE)
+#define gnt_vline_new() gnt_line_new(TRUE)
+
 GntWidget *gnt_line_new(gboolean vertical);
 
 G_END_DECLS
--- a/console/libgnt/gntmain.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gntmain.c	Wed Jul 19 07:12:59 2006 +0000
@@ -258,14 +258,16 @@
 	{
 		GntBox *box = GNT_BOX(iter->data);
 
-		gnt_tree_add_row_after(GNT_TREE(tree), box, box->title, NULL, NULL);
+		gnt_tree_add_row_after(GNT_TREE(tree), box,
+				gnt_tree_create_row(GNT_TREE(tree), box->title), NULL, NULL);
 		update_window_in_list(GNT_WIDGET(box));
 	}
 
 	gnt_tree_set_selected(GNT_TREE(tree), focus_list->data);
 	gnt_box_add_widget(GNT_BOX(win), tree);
 
-	gnt_widget_set_size(tree, getmaxx(stdscr) / 3, getmaxy(stdscr) / 2);
+	gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3);
+	gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2);
 	gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4);
 
 	lock_focus_list = 1;
@@ -539,7 +541,6 @@
 		ascii_only = TRUE;
 
 	initscr();
-	start_color();
 	gnt_init_colors();
 
 	X_MIN = 0;
@@ -602,7 +603,8 @@
 				&& GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS))
 		{
 			gnt_tree_add_row_after(GNT_TREE(window_list.tree), widget,
-					GNT_BOX(widget)->title, NULL, NULL);
+					gnt_tree_create_row(GNT_TREE(window_list.tree), GNT_BOX(widget)->title),
+					NULL, NULL);
 			update_window_in_list(widget);
 		}
 	}
@@ -692,6 +694,7 @@
 
 void gnt_quit()
 {
+	gnt_uninit_colors();
 	endwin();
 }
 
--- a/console/libgnt/gnttextview.h	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gnttextview.h	Wed Jul 19 07:12:59 2006 +0000
@@ -30,6 +30,7 @@
 
 typedef enum
 {
+	GNT_TEXT_FLAG_NORMAL      = 0,
 	GNT_TEXT_FLAG_BOLD        = 1 << 0,
 	GNT_TEXT_FLAG_UNDERLINE   = 1 << 1,
 	GNT_TEXT_FLAG_BLINK       = 1 << 2,
--- a/console/libgnt/gnttree.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gnttree.c	Wed Jul 19 07:12:59 2006 +0000
@@ -18,7 +18,6 @@
 struct _GnTreeRow
 {
 	void *key;
-	char *text;
 	void *data;		/* XXX: unused */
 
 	gboolean collapsed;
@@ -31,6 +30,14 @@
 	GntTreeRow *child;
 	GntTreeRow *next;
 	GntTreeRow *prev;
+
+	GList *columns;
+};
+
+struct _GnTreeCol
+{
+	char *text;
+	int span;       /* How many columns does it span? */
 };
 
 static GntWidgetClass *parent_class = NULL;
@@ -151,6 +158,83 @@
 	return (hb - ha);
 }
 
+static int
+find_depth(GntTreeRow *row)
+{
+	int dep = -1;
+
+	while (row)
+	{
+		dep++;
+		row = row->parent;
+	}
+
+	return dep;
+}
+
+static char *
+update_row_text(GntTree *tree, GntTreeRow *row)
+{
+	GString *string = g_string_new(NULL);
+	GList *iter;
+	int i;
+
+	for (i = 0, iter = row->columns; i < tree->ncol && iter; i++, iter = iter->next)
+	{
+		GntTreeCol *col = iter->data;
+		char *text;
+		int len = g_utf8_strlen(col->text, -1);
+		int fl = 0;
+		gboolean ell = FALSE;
+
+		if (i == 0)
+		{
+			if (row->choice)
+			{
+				g_string_append_printf(string, "[%c] ",
+						row->isselected ? 'X' : ' ');
+				fl = 4;
+			}
+			else if (row->parent == NULL && row->child)
+			{
+				if (row->collapsed)
+				{
+					string = g_string_append(string, "+ ");
+				}
+				else
+				{
+					string = g_string_append(string, "- ");
+				}
+				fl = 2;
+			}
+			else
+			{
+				fl = TAB_SIZE * find_depth(row);
+				g_string_append_printf(string, "%*s", fl, "");
+			}
+			len += fl;
+		}
+		else
+			g_string_append_c(string, '|');
+
+		if (len > tree->columns[i].width)
+		{
+			len = tree->columns[i].width;
+			ell = TRUE;
+		}
+
+		text = g_utf8_offset_to_pointer(col->text, len - fl - ell);
+		string = g_string_append_len(string, col->text, text - col->text);
+		if (len < tree->columns[i].width)
+			g_string_append_printf(string, "%*s", tree->columns[i].width - len, "");
+		else if (ell)
+		{
+			g_string_append_unichar(string, (gunichar)2026);
+		}
+	}
+	return g_string_free(string, FALSE);
+}
+
 static void
 redraw_tree(GntTree *tree)
 {
@@ -158,7 +242,6 @@
 	GntWidget *widget = GNT_WIDGET(tree);
 	GntTreeRow *row;
 	int pos;
-	gboolean deep;
 
 	if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
 		pos = 0;
@@ -172,36 +255,17 @@
 
 	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
 
-	deep = TRUE;
 	row = tree->top;
 	for (start = pos; row && start < widget->priv.height - pos;
 				start++, row = get_next(row))
 	{
-		char str[2048];
+		char *str;
 		int wr;
-		char format[16] = "";
 
 		GntTextFormatFlags flags = row->flags;
 		int attr = 0;
 
-		deep = TRUE;
-
-		if (row->parent == NULL && row->child)
-		{
-			if (row->collapsed)
-			{
-				strcpy(format, "+ ");
-				deep = FALSE;
-			}
-			else
-				strcpy(format, "- ");
-		}
-		else if (row->choice)
-		{
-			g_snprintf(format, sizeof(format) - 1, "[%c] ", row->isselected ? 'X' : ' ');
-		}
-
-		g_snprintf(str, sizeof(str) - 1, "%s%s", format, row->text);
+		str = update_row_text(tree, row);
 
 		if ((wr = g_utf8_strlen(str, -1)) >= widget->priv.width - 1 - pos)
 		{
@@ -238,6 +302,7 @@
 		mvwprintw(widget->window, start, pos, str);
 		whline(widget->window, ' ', widget->priv.width - pos * 2 - g_utf8_strlen(str, -1));
 		tree->bottom = row;
+		g_free(str);
 	}
 
 	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
@@ -267,7 +332,13 @@
 	if (widget->priv.height == 0)
 		widget->priv.height = 10;	/* XXX: Why?! */
 	if (widget->priv.width == 0)
-		widget->priv.width = 20;	/* YYY: 'cuz ... */
+	{
+		GntTree *tree = GNT_TREE(widget);
+		int i, width = 0;
+		for (i = 0; i < tree->ncol; i++)
+			width += tree->columns[i].width;
+		widget->priv.width = width + i;
+	}
 }
 
 static void
@@ -437,6 +508,15 @@
 }
 
 static void
+free_tree_col(gpointer data)
+{
+	GntTreeCol *col = data;
+
+	g_free(col->text);
+	g_free(col);
+}
+
+static void
 free_tree_row(gpointer data)
 {
 	GntTreeRow *row = data;
@@ -444,20 +524,14 @@
 	if (!row)
 		return;
 
-	g_free(row->text);
+	g_list_foreach(row->columns, (GFunc)free_tree_col, NULL);
+	g_list_free(row->columns);
 	g_free(row);
 }
 
 GntWidget *gnt_tree_new()
 {
-	GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL);
-	GntTree *tree = GNT_TREE(widget);
-
-	tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
-	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW);
-	gnt_widget_set_take_focus(widget, TRUE);
-
-	return widget;
+	return gnt_tree_new_with_columns(1);
 }
 
 void gnt_tree_set_visible_rows(GntTree *tree, int rows)
@@ -500,23 +574,9 @@
 	g_signal_emit(tree, signals[SIG_SCROLLED], 0, count);
 }
 
-static int
-find_depth(GntTreeRow *row)
+GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro)
 {
-	int dep = -1;
-
-	while (row)
-	{
-		dep++;
-		row = row->parent;
-	}
-
-	return dep;
-}
-
-GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, const char *text, void *parent, void *bigbro)
-{
-	GntTreeRow *row = g_new0(GntTreeRow, 1), *pr = NULL;
+	GntTreeRow *pr = NULL;
 
 	g_hash_table_replace(tree->hash, key, row);
 
@@ -575,7 +635,6 @@
 	}
 
 	row->key = key;
-	row->text = g_strdup_printf("%*s%s", TAB_SIZE * find_depth(row), "", text);
 	row->data = NULL;
 
 	if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_MAPPED))
@@ -591,10 +650,10 @@
 	return NULL;
 }
 
-const char *gnt_tree_get_selection_text(GntTree *tree)
+char *gnt_tree_get_selection_text(GntTree *tree)
 {
 	if (tree->current)
-		return tree->current->text;
+		update_row_text(tree, tree->current);
 	return NULL;
 }
 
@@ -657,27 +716,32 @@
 			!!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER));
 }
 
-void gnt_tree_change_text(GntTree *tree, gpointer key, const char *text)
+void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text)
 {
-	GntTreeRow *row = g_hash_table_lookup(tree->hash, key);
+	GntTreeRow *row;
+	GntTreeCol *col;
+
+	g_return_if_fail(colno < tree->ncol);
+	
+	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);
+		col = g_list_nth_data(row->columns, colno);
+		g_free(col->text);
+		col->text = g_strdup(text);
 
 		if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) > 0)
 			redraw_tree(tree);
 	}
 }
 
-GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, const char *text, void *parent, void *bigbro)
+GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro)
 {
-	GntTreeRow *row;
-
-	row = g_hash_table_lookup(tree->hash, key);
-	g_return_val_if_fail(!row || !row->choice, NULL);
+	GntTreeRow *r;
+	r = g_hash_table_lookup(tree->hash, key);
+	g_return_val_if_fail(!r || !r->choice, NULL);
 		
-	row = gnt_tree_add_row_after(tree, key, text, parent, bigbro);
+	row = gnt_tree_add_row_after(tree, key, row, parent, bigbro);
 	row->choice = TRUE;
 
 	return row;
@@ -737,3 +801,49 @@
 		redraw_tree(tree);
 }
 
+GntWidget *gnt_tree_new_with_columns(int col)
+{
+	GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL);
+	GntTree *tree = GNT_TREE(widget);
+
+	tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
+	tree->ncol = col;
+	tree->columns = g_new0(struct _GntTreeColInfo, col);
+	while (col--)
+	{
+		tree->columns[col].width = 15;
+	}
+	
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW);
+	gnt_widget_set_take_focus(widget, TRUE);
+
+	return widget;
+}
+
+GntTreeRow *gnt_tree_create_row(GntTree *tree, ...)
+{
+	GntTreeRow *row = g_new0(GntTreeRow, 1);
+	int i;
+	va_list args;
+
+	va_start(args, tree);
+
+	for (i = 0; i < tree->ncol; i++)
+	{
+		GntTreeCol *col = g_new0(GntTreeCol, 1);
+		col->span = 1;
+		col->text = g_strdup(va_arg(args, const char *));
+
+		row->columns = g_list_append(row->columns, col);
+	}
+
+	return row;
+}
+
+void gnt_tree_set_col_width(GntTree *tree, int col, int width)
+{
+	g_return_if_fail(col < tree->ncol);
+
+	tree->columns[col].width = width;
+}
+
--- a/console/libgnt/gnttree.h	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/gnttree.h	Wed Jul 19 07:12:59 2006 +0000
@@ -23,20 +23,28 @@
 typedef struct _GnTreeClass		GntTreeClass;
 
 typedef struct _GnTreeRow		GntTreeRow;
+typedef struct _GnTreeCol		GntTreeCol;
 
 struct _GnTree
 {
 	GntWidget parent;
 
-	GntTreeRow *current;	/* current selection */
+	GntTreeRow *current;    /* current selection */
 
-	GntTreeRow *top;		/* The topmost visible item */
-	GntTreeRow *bottom;		/* The bottommost visible item */
+	GntTreeRow *top;        /* The topmost visible item */
+	GntTreeRow *bottom;     /* The bottommost visible item */
+	
+	GntTreeRow *root;       /* The root of all evil */
 	
-	GntTreeRow *root; /* The root of all evil */
-	
-	GList *list;	/* List of GntTreeRow s */
-	GHashTable *hash;	/* XXX: We may need this for quickly referencing the rows */
+	GList *list;            /* List of GntTreeRow s */
+	GHashTable *hash;       /* XXX: We may need this for quickly referencing the rows */
+
+	int ncol;               /* No. of columns */
+	struct _GntTreeColInfo
+	{
+		int width;
+		int *name;
+	} *columns;             /* Would a GList be better? */
 };
 
 struct _GnTreeClass
@@ -55,7 +63,9 @@
 
 GType gnt_tree_get_gtype(void);
 
-GntWidget *gnt_tree_new();
+GntWidget *gnt_tree_new();      /* A tree with just one column */
+
+GntWidget *gnt_tree_new_with_columns(int columns);
 
 void gnt_tree_set_visible_rows(GntTree *tree, int rows);
 
@@ -63,20 +73,20 @@
 
 void gnt_tree_scroll(GntTree *tree, int count);
 
-GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, const char *text, void *parent, void *bigbro);
+GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
 
 gpointer gnt_tree_get_selection_data(GntTree *tree);
 
-const char *gnt_tree_get_selection_text(GntTree *tree);
+char *gnt_tree_get_selection_text(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);
+void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text);
 
-GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, const char *text, void *parent, void *bigbro);
+GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
 
 void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set);
 
@@ -86,6 +96,10 @@
 
 void gnt_tree_set_selected(GntTree *tree , void *key);
 
+GntTreeRow *gnt_tree_create_row(GntTree *tree, ...);
+
+void gnt_tree_set_col_width(GntTree *tree, int col, int width);
+
 G_END_DECLS
 
 #endif /* GNT_TREE_H */
--- a/console/libgnt/test/focus.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/test/focus.c	Wed Jul 19 07:12:59 2006 +0000
@@ -48,11 +48,11 @@
 	gnt_box_add_widget(GNT_BOX(box), tree);
 	gnt_box_add_widget(GNT_BOX(hbox), box);
 
-	gnt_tree_add_row_after(GNT_TREE(tree), "a", "a", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "c", "c", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "d", "d", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "e", "e", "a", NULL);
-	gnt_tree_add_choice(GNT_TREE(tree), "b", "b", "d", NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "a", gnt_tree_create_row(GNT_TREE(tree), "a"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "c", gnt_tree_create_row(GNT_TREE(tree), "c"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "d", gnt_tree_create_row(GNT_TREE(tree), "d"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "e", gnt_tree_create_row(GNT_TREE(tree), "e"), "a", NULL);
+	gnt_tree_add_choice(GNT_TREE(tree), "b", gnt_tree_create_row(GNT_TREE(tree), "b"), "d", NULL);
 
 	GNT_WIDGET_UNSET_FLAGS(hbox, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
 	gnt_box_set_title(GNT_BOX(hbox), "This is the title …");
--- a/console/libgnt/test/multiwin.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/test/multiwin.c	Wed Jul 19 07:12:59 2006 +0000
@@ -43,19 +43,19 @@
 	gnt_widget_set_position(box2, 35, 15);
 	gnt_widget_show(box2);
 
-	gnt_tree_add_row_after(GNT_TREE(tree), "a", "a", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "c", "c", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "d", "d", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "e", "e", "a", NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "b", "b", "d", NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "a", gnt_tree_create_row(GNT_TREE(tree), "a"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "c", gnt_tree_create_row(GNT_TREE(tree), "c"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "d", gnt_tree_create_row(GNT_TREE(tree), "d"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "e", gnt_tree_create_row(GNT_TREE(tree), "e"), "a", NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "b", gnt_tree_create_row(GNT_TREE(tree), "b"), "d", NULL);
 
-	gnt_tree_add_choice(GNT_TREE(tree), "1", "1", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "2", "2", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "3", "3", NULL, NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "4", "4", "a", NULL);
-	gnt_tree_add_row_after(GNT_TREE(tree), "5", "5", "d", NULL);
+	gnt_tree_add_choice(GNT_TREE(tree), "1", gnt_tree_create_row(GNT_TREE(tree), "1"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "2", gnt_tree_create_row(GNT_TREE(tree), "2"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "3", gnt_tree_create_row(GNT_TREE(tree), "3"), NULL, NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "4", gnt_tree_create_row(GNT_TREE(tree), "4"), "a", NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "5", gnt_tree_create_row(GNT_TREE(tree), "5"), "d", NULL);
 
-	gnt_tree_add_row_after(GNT_TREE(tree), "6", "6", "4", NULL);
+	gnt_tree_add_row_after(GNT_TREE(tree), "6", gnt_tree_create_row(GNT_TREE(tree), "6"), "4", NULL);
 
 	gnt_tree_set_row_flags(GNT_TREE(tree), "e", GNT_TEXT_FLAG_DIM);
 
--- a/console/libgnt/test/wm.c	Tue Jul 18 07:18:05 2006 +0000
+++ b/console/libgnt/test/wm.c	Wed Jul 19 07:12:59 2006 +0000
@@ -20,8 +20,9 @@
 		handle = g_module_open(cmd, G_MODULE_BIND_LOCAL);
 		if (handle && g_module_symbol(handle, "main", (gpointer)&func))
 		{
+			char *argv[] = {cmd, NULL};
 			gnt_entry_clear(entry);
-			func();
+			func(1, argv);
 		}
 		else
 		{
@@ -29,6 +30,7 @@
 			gnt_box_set_toplevel(GNT_BOX(widget), TRUE);
 			gnt_box_set_title(GNT_BOX(widget), "Error");
 			gnt_box_add_widget(GNT_BOX(widget), gnt_label_new("Could not execute."));
+			gnt_box_add_widget(GNT_BOX(widget), gnt_label_new(g_module_error()));
 
 			gnt_widget_show(widget);
 		}