changeset 19005:2e064085b7df

propagate from branch 'im.pidgin.pidgin' (head 1ede3f4c61fab6e6979e40b2169494d27af5f528) to branch 'im.pidgin.soc.2007.certmgr' (head 27afef9aea7ea6baa4c0bcc8ddc0dcb3282d747e)
author William Ehlhardt <williamehlhardt@gmail.com>
date Sun, 08 Jul 2007 04:27:02 +0000
parents 45865fb3f4f9 (diff) d4065b26dcac (current diff)
children dc60287ce426
files
diffstat 35 files changed, 693 insertions(+), 230 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Jul 08 04:23:55 2007 +0000
+++ b/ChangeLog	Sun Jul 08 04:27:02 2007 +0000
@@ -22,6 +22,7 @@
 	* A new status area has been added to the top of conversations to
 	  provide additional detail about the buddy, including buddy icon,
 	  protocol and status message.
+	* Show idle times in the buddy list as days, hours, seconds
 
 	Finch:
 	* There's support for workspaces now (details in the manpage)
@@ -29,6 +30,7 @@
 	* Some improvements for tab-completion, tooltip and the password entries
 	* Some bugs regarding search results fixed
 	* A new DBus-script to create a docklet for finch
+	* Support for showing empty groups in the buddy list (Eric Polino)
 
 version 2.0.2 (06/14/2007):
 	Pidgin:
--- a/finch/gntaccount.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/gntaccount.c	Sun Jul 08 04:27:02 2007 +0000
@@ -472,6 +472,14 @@
 		}
 	}
 
+	list = purple_plugins_get_protocols();
+	if (list == NULL) {
+		purple_notify_error(NULL, _("Error"),
+				_("There's no protocol plugins installed."),
+				_("(You probably forgot to 'make install'.)"));
+		return;
+	}
+
 	dialog = g_new0(AccountEditDialog, 1);
 	accountdialogs = g_list_prepend(accountdialogs, dialog);
 
@@ -489,7 +497,6 @@
 	gnt_box_add_widget(GNT_BOX(window), hbox);
 
 	dialog->protocol = combo = gnt_combo_box_new();
-	list = purple_plugins_get_protocols();
 	for (iter = list; iter; iter = iter->next)
 	{
 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data,
--- a/finch/gntblist.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/gntblist.c	Sun Jul 08 04:27:02 2007 +0000
@@ -195,8 +195,8 @@
 			node_update(list, (PurpleBlistNode*)contact);
 	} else if (!PURPLE_BLIST_NODE_IS_GROUP(node)) {
 		PurpleGroup *group = (PurpleGroup*)node->parent;
-		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) ||
-				group->currentsize < 1)
+		if ((group->currentsize < 1 && !purple_prefs_get_bool(PREF_ROOT "/emptygroups")) ||
+				(!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)))
 			node_remove(list, node->parent);
 		for (node = node->child; node; node = node->next)
 			node->ui_data = NULL;
@@ -253,8 +253,9 @@
 		}
 	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
 		PurpleGroup *group = (PurpleGroup*)node;
-		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) ||
-				group->currentsize < 1)
+		if (!purple_prefs_get_bool(PREF_ROOT "/emptygroups") &&
+				((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) ||
+				 group->currentsize < 1))
 			node_remove(list, node);
 		else
 			add_node(node, list->ui_data);
@@ -507,21 +508,24 @@
 		gboolean ascii = gnt_ascii_only();
 		
 		presence = purple_buddy_get_presence(buddy);
-		now = purple_presence_get_active_status(presence);
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
+			strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1);
+		else {
+			now = purple_presence_get_active_status(presence);
 
-		prim = purple_status_type_get_primitive(purple_status_get_type(now));
+			prim = purple_status_type_get_primitive(purple_status_get_type(now));
 
-		switch(prim)
-		{
-			case PURPLE_STATUS_OFFLINE:
-				strncpy(status, ascii ? "x" : "⊗", sizeof(status) - 1);
-				break;
-			case PURPLE_STATUS_AVAILABLE:
-				strncpy(status, ascii ? "o" : "◯", sizeof(status) - 1);
-				break;
-			default:
-				strncpy(status, ascii ? "." : "⊖", sizeof(status) - 1);
-				break;
+			switch(prim) {
+				case PURPLE_STATUS_OFFLINE:
+					strncpy(status, ascii ? "x" : "⊗", sizeof(status) - 1);
+					break;
+				case PURPLE_STATUS_AVAILABLE:
+					strncpy(status, ascii ? "o" : "◯", sizeof(status) - 1);
+					break;
+				default:
+					strncpy(status, ascii ? "." : "⊖", sizeof(status) - 1);
+					break;
+			}
 		}
 		name = purple_buddy_get_alias(buddy);
 	}
@@ -590,6 +594,9 @@
 	if (node->ui_data)
 		return;
 
+	if (!purple_account_is_connected(buddy->account))
+		return;
+
 	contact = (PurpleContact*)node->parent;
 	if (!contact)   /* When a new buddy is added and show-offline is set */
 		return;
@@ -1266,12 +1273,14 @@
 	PurplePluginProtocolInfo *prpl_info;
 	PurpleAccount *account;
 	PurpleNotifyUserInfo *user_info;
+	PurplePresence *presence;
 	const char *alias = purple_buddy_get_alias(buddy);
 	char *tmp, *strip;
 
 	user_info = purple_notify_user_info_new();
 
 	account = purple_buddy_get_account(buddy);
+	presence = purple_buddy_get_presence(buddy);
 
 	if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias))
 		purple_notify_user_info_add_pair(user_info, _("Nickname"), alias);
@@ -1305,6 +1314,10 @@
 
 	strip = purple_markup_strip_html(tmp);
 	g_string_append(str, strip);
+
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
+		g_string_append(str, _("On Mobile"));
+
 	g_free(strip);
 	g_free(tmp);
 }
@@ -1714,9 +1727,12 @@
 	purple_prefs_add_int(PREF_ROOT "/position/y", 0);
 	purple_prefs_add_bool(PREF_ROOT "/idletime", TRUE);
 	purple_prefs_add_bool(PREF_ROOT "/showoffline", FALSE);
+	purple_prefs_add_bool(PREF_ROOT "/emptygroups", FALSE);
 	purple_prefs_add_string(PREF_ROOT "/sort_type", "text");
 
 	purple_prefs_connect_callback(finch_blist_get_handle(),
+			PREF_ROOT "/emptygroups", redraw_blist, NULL);
+	purple_prefs_connect_callback(finch_blist_get_handle(),
 			PREF_ROOT "/showoffline", redraw_blist, NULL);
 	purple_prefs_connect_callback(finch_blist_get_handle(),
 			PREF_ROOT "/sort_type", redraw_blist, NULL);
@@ -2124,10 +2140,9 @@
 	}
 }
 
-static void show_offline_cb(GntMenuItem *item, gpointer n)
+static void toggle_pref_cb(GntMenuItem *item, gpointer n)
 {
-	purple_prefs_set_bool(PREF_ROOT "/showoffline",
-		!purple_prefs_get_bool(PREF_ROOT "/showoffline"));
+	purple_prefs_set_bool(n, !purple_prefs_get_bool(n));
 }
 
 static void sort_blist_change_cb(GntMenuItem *item, gpointer n)
@@ -2135,7 +2150,7 @@
 	purple_prefs_set_string(PREF_ROOT "/sort_type", n);
 }
 
-/* XXX: send_im_select* -- Xerox */
+/* send_im_select* -- Xerox */
 static void
 send_im_select_cb(gpointer data, PurpleRequestFields *fields)
 {
@@ -2208,11 +2223,17 @@
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL);
 
+	item = gnt_menuitem_check_new(_("Show empty groups"));
+	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
+				purple_prefs_get_bool(PREF_ROOT "/emptygroups"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups");
+	
 	item = gnt_menuitem_check_new(_("Show offline buddies"));
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
 				purple_prefs_get_bool(PREF_ROOT "/showoffline"));
 	gnt_menu_add_item(GNT_MENU(sub), item);
-	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), show_offline_cb, NULL);
+	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/showoffline");
 
 	item = gnt_menuitem_new(_("Sort by status"));
 	gnt_menu_add_item(GNT_MENU(sub), item);
--- a/finch/gntconv.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/gntconv.c	Sun Jul 08 04:27:02 2007 +0000
@@ -541,6 +541,8 @@
 	gnt_entry_set_always_suggest(GNT_ENTRY(ggc->entry), FALSE);
 
 	gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry);
+	gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry);
+
 	g_signal_connect_after(G_OBJECT(ggc->entry), "key_pressed", G_CALLBACK(entry_key_pressed), ggc);
 	g_signal_connect(G_OBJECT(ggc->entry), "completion", G_CALLBACK(completion_cb), NULL);
 	g_signal_connect(G_OBJECT(ggc->window), "destroy", G_CALLBACK(closing_window), ggc);
--- a/finch/gntdebug.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/gntdebug.c	Sun Jul 08 04:27:02 2007 +0000
@@ -274,6 +274,7 @@
 
 	g_signal_connect(G_OBJECT(debug.window), "destroy", G_CALLBACK(reset_debug_win), NULL);
 	gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
+	gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
 
 	gnt_widget_show(debug.window);
 }
--- a/finch/libgnt/gnt.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gnt.h	Sun Jul 08 04:27:02 2007 +0000
@@ -112,7 +112,7 @@
 /**
  * 
  * @param label
- * @param callback)()
+ * @param callback
  */
 void gnt_register_action(const char *label, void (*callback)());
 
@@ -149,3 +149,11 @@
  */
 void gnt_set_clipboard_string(gchar *string);
 
+/**
+ * Spawn a different application that will consume the console.
+ */
+gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
+		gint *stin, gint *stout, gint *sterr);
+
+gboolean gnt_is_refugee(void);
+
--- a/finch/libgnt/gntcombobox.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntcombobox.c	Sun Jul 08 04:27:02 2007 +0000
@@ -107,7 +107,7 @@
 		GntWidget *dd = GNT_COMBO_BOX(widget)->dropdown;
 		gnt_widget_size_request(dd);
 		widget->priv.height = 3;   /* For now, a combobox will have border */
-		widget->priv.width = MAX(10, dd->priv.width + 4);
+		widget->priv.width = MAX(10, dd->priv.width + 2);
 	}
 }
 
--- a/finch/libgnt/gntfilesel.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntfilesel.c	Sun Jul 08 04:27:02 2007 +0000
@@ -448,6 +448,9 @@
 	GntFileSel *sel = GNT_FILE_SEL(widget);
 	GntWidget *hbox, *vbox;
 
+	if (sel->current == NULL)
+		gnt_file_sel_set_current_location(sel, g_get_home_dir());
+
 	vbox = gnt_vbox_new(FALSE);
 	gnt_box_set_pad(GNT_BOX(vbox), 0);
 	gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_MID);
@@ -584,7 +587,7 @@
 					 G_STRUCT_OFFSET(GntFileSelClass, file_selected),
 					 NULL, NULL,
 					 gnt_closure_marshal_VOID__STRING_STRING,
-					 G_TYPE_NONE, 0);
+					 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 
 	gnt_bindable_class_register_action(bindable, "toggle-tag", toggle_tag_selection, "t", NULL);
 	gnt_bindable_class_register_action(bindable, "clear-tags", clear_tags, "c", NULL);
@@ -631,6 +634,16 @@
 	return type;
 }
 
+static void
+select_activated_cb(GntWidget *button, GntFileSel *sel)
+{
+	char *path = gnt_file_sel_get_selected_file(sel);
+	char *file = g_path_get_basename(path);
+	g_signal_emit(sel, signals[SIG_FILE_SELECTED], 0, path, file);
+	g_free(file);
+	g_free(path);
+}
+
 GntWidget *gnt_file_sel_new(void)
 {
 	GntWidget *widget = g_object_new(GNT_TYPE_FILE_SEL, NULL);
@@ -650,6 +663,7 @@
 	gnt_tree_set_show_title(GNT_TREE(sel->files), TRUE);
 	gnt_tree_set_col_width(GNT_TREE(sel->files), 0, 25);
 	gnt_tree_set_col_width(GNT_TREE(sel->files), 1, 10);
+	gnt_tree_set_column_is_right_aligned(GNT_TREE(sel->files), 1, TRUE);
 	g_signal_connect(G_OBJECT(sel->files), "selection_changed", G_CALLBACK(file_sel_changed), sel);
 
 	/* The location entry */
@@ -659,6 +673,8 @@
 	sel->cancel = gnt_button_new("Cancel");
 	sel->select = gnt_button_new("Select");
 
+	g_signal_connect(G_OBJECT(sel->select), "activate", G_CALLBACK(select_activated_cb), sel);
+
 	return widget;
 }
 
--- a/finch/libgnt/gntkeys.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntkeys.c	Sun Jul 08 04:27:02 2007 +0000
@@ -154,7 +154,7 @@
 
 const char *gnt_key_translate(const char *name)
 {
-	return g_hash_table_lookup(specials, name);
+	return name ? g_hash_table_lookup(specials, name) : NULL;
 }
 
 typedef struct {
--- a/finch/libgnt/gntmain.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntmain.c	Sun Jul 08 04:27:02 2007 +0000
@@ -65,6 +65,7 @@
  */
 
 static GIOChannel *channel = NULL;
+static int channel_read_callback;
 
 static gboolean ascii_only;
 static gboolean mouse_enabled;
@@ -220,8 +221,13 @@
 io_invoke(GIOChannel *source, GIOCondition cond, gpointer null)
 {
 	char keys[256];
-	int rd = read(STDIN_FILENO, keys + HOLDING_ESCAPE, sizeof(keys) - 1 - HOLDING_ESCAPE);
+	int rd;
 	char *k;
+
+	if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD)
+		return FALSE;
+
+	rd = read(STDIN_FILENO, keys + HOLDING_ESCAPE, sizeof(keys) - 1 - HOLDING_ESCAPE);
 	if (rd < 0)
 	{
 		int ch = getch(); /* This should return ERR, but let's see what it really returns */
@@ -288,7 +294,7 @@
 	g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL );
 #endif
 
-	result = g_io_add_watch_full(channel,  G_PRIORITY_HIGH,
+	channel_read_callback = result = g_io_add_watch_full(channel,  G_PRIORITY_HIGH,
 					(G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
 					io_invoke, NULL, NULL);
 	
@@ -346,6 +352,14 @@
 	static GntWidget *win = NULL;
 	GntWidget *bbox, *button;
 
+	if (wm->menu) {
+		do {
+			gnt_widget_hide(GNT_WIDGET(wm->menu));
+			if (wm->menu)
+				wm->menu = wm->menu->parentmenu;
+		} while (wm->menu);
+	}
+
 	if (win)
 		goto raise;
 
@@ -622,7 +636,51 @@
 {
 	return clipboard;
 }
+
 gchar *gnt_get_clipboard_string()
 {
 	return gnt_clipboard_get_string(clipboard);
 }
+
+#if GLIB_CHECK_VERSION(2,4,0)
+static void
+reap_child(GPid pid, gint status, gpointer data)
+{
+	wm->mode = GNT_KP_MODE_NORMAL;
+	clear();
+	setup_io();
+	refresh_screen();
+}
+#endif
+
+gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
+		gint *stin, gint *stout, gint *sterr)
+{
+#if GLIB_CHECK_VERSION(2,4,0)
+	GPid pid = 0;
+
+	if (!g_spawn_async_with_pipes(wd, argv, envp,
+			G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+			(GSpawnChildSetupFunc)endwin, NULL,
+			&pid, stin, stout, sterr, NULL))
+		return FALSE;
+
+	g_source_remove(channel_read_callback);
+	wm->mode = GNT_KP_MODE_WAIT_ON_CHILD;
+	g_child_watch_add(pid, reap_child, NULL);
+
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+gboolean gnt_is_refugee()
+{
+#if GLIB_CHECK_VERSION(2,4,0)
+	return (wm && wm->mode == GNT_KP_MODE_WAIT_ON_CHILD);
+#else
+	return FALSE;
+#endif
+}
+
--- a/finch/libgnt/gntmenu.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Sun Jul 08 04:27:02 2007 +0000
@@ -23,6 +23,7 @@
 #include "gntmenu.h"
 #include "gntmenuitemcheck.h"
 
+#include <ctype.h>
 #include <string.h>
 
 enum
@@ -30,11 +31,20 @@
 	SIGS = 1,
 };
 
+enum
+{
+	ITEM_TEXT = 0,
+	ITEM_TRIGGER,
+	ITEM_SUBMENU,
+	NUM_COLUMNS
+};
+
 static GntTreeClass *parent_class = NULL;
 
 static void (*org_draw)(GntWidget *wid);
 static void (*org_destroy)(GntWidget *wid);
 static void (*org_map)(GntWidget *wid);
+static void (*org_size_request)(GntWidget *wid);
 static gboolean (*org_key_pressed)(GntWidget *w, const char *t);
 
 static void
@@ -75,21 +85,26 @@
 		widget->priv.height = 1;
 		widget->priv.width = getmaxx(stdscr);
 	} else {
+		org_size_request(widget);
 		widget->priv.height = g_list_length(menu->list) + 2;
-		widget->priv.width = 25;  /* XXX: */
 	}
 }
 
 static void
 menu_tree_add(GntMenu *menu, GntMenuItem *item, GntMenuItem *parent)
 {
+	char trigger[4] = "\0 )\0";
+
+	if ((trigger[1] = gnt_menuitem_get_trigger(item)) && trigger[1] != ' ')
+		trigger[0] = '(';
+
 	if (GNT_IS_MENU_ITEM_CHECK(item)) {
 		gnt_tree_add_choice(GNT_TREE(menu), item,
-			gnt_tree_create_row(GNT_TREE(menu), item->text, " "), parent, NULL);
+			gnt_tree_create_row(GNT_TREE(menu), item->text, trigger, " "), parent, NULL);
 		gnt_tree_set_choice(GNT_TREE(menu), item, gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
 	} else
 		gnt_tree_add_row_last(GNT_TREE(menu), item,
-			gnt_tree_create_row(GNT_TREE(menu), item->text, item->submenu ? ">" : " "), parent);
+			gnt_tree_create_row(GNT_TREE(menu), item->text, trigger, item->submenu ? ">" : " "), parent);
 
 	if (0 && item->submenu) {
 		GntMenu *sub = GNT_MENU(item->submenu);
@@ -101,6 +116,45 @@
 	}
 }
 
+#define GET_VAL(ch)  ((ch >= '0' && ch <= '9') ? (ch - '0') : (ch >= 'a' && ch <= 'z') ? (10 + ch - 'a') : 36)
+
+static void
+assign_triggers(GntMenu *menu)
+{
+	GList *iter;
+	gboolean bools[37];
+
+	memset(bools, 0, sizeof(bools));
+	bools[36] = 1;
+
+	for (iter = menu->list; iter; iter = iter->next) {
+		GntMenuItem *item = iter->data;
+		char trigger = tolower(gnt_menuitem_get_trigger(item));
+		if (trigger == '\0' || trigger == ' ')
+			continue;
+		bools[(int)GET_VAL(trigger)] = 1;
+	}
+
+	for (iter = menu->list; iter; iter = iter->next) {
+		GntMenuItem *item = iter->data;
+		char trigger = gnt_menuitem_get_trigger(item);
+		const char *text = item->text;
+		if (trigger != '\0')
+			continue;
+		while (*text) {
+			char ch = tolower(*text++);
+			if (ch == ' ' || bools[(int)GET_VAL(ch)])
+				continue;
+			trigger = ch;
+			break;
+		}
+		if (trigger == 0)
+			trigger = item->text[0];
+		gnt_menuitem_set_trigger(item, trigger);
+		bools[(int)GET_VAL(trigger)] = 1;
+	}
+}
+
 static void
 gnt_menu_map(GntWidget *widget)
 {
@@ -112,6 +166,8 @@
 		/* Populate the tree */
 		GList *iter;
 		gnt_tree_remove_all(GNT_TREE(widget));
+		/* Try to assign some trigger for the items */
+		assign_triggers(menu);
 		for (iter = menu->list; iter; iter = iter->next) {
 			GntMenuItem *item = GNT_MENU_ITEM(iter->data);
 			menu_tree_add(menu, item, NULL);
@@ -149,6 +205,41 @@
 	}
 }
 
+static GList*
+find_item_with_trigger(GList *start, GList *end, char trigger)
+{
+	GList *iter;
+	for (iter = start; iter != (end ? end : NULL); iter = iter->next) {
+		if (gnt_menuitem_get_trigger(iter->data) == trigger)
+			return iter;
+	}
+	return NULL;
+}
+
+static gboolean
+check_for_trigger(GntMenu *menu, char trigger)
+{
+	/* check for a trigger key */
+	GList *iter;
+	GList *nth = g_list_find(menu->list, gnt_tree_get_selection_data(GNT_TREE(menu)));
+	GList *find = find_item_with_trigger(nth->next, NULL, trigger);
+	if (!find)
+		find = find_item_with_trigger(menu->list, nth->next, trigger);
+	if (!find)
+		return FALSE;
+	if (find != nth) {
+		gnt_tree_set_selected(GNT_TREE(menu), find->data);
+		iter = find_item_with_trigger(find->next, NULL, trigger);
+		if (iter != NULL && iter != find)
+			return TRUE;
+		iter = find_item_with_trigger(menu->list, nth, trigger);
+		if (iter != NULL && iter != find)
+			return TRUE;
+	}
+	gnt_widget_activate(GNT_WIDGET(menu));
+	return TRUE;
+}
+
 static gboolean
 gnt_menu_key_pressed(GntWidget *widget, const char *text)
 {
@@ -189,6 +280,10 @@
 			return TRUE;
 		}
 	} else {
+		if (text[1] == '\0') {
+			if (check_for_trigger(menu, text[0]))
+				return TRUE;
+		}
 		return org_key_pressed(widget, text);
 	}
 
@@ -260,6 +355,7 @@
 	org_map = wid_class->map;
 	org_draw = wid_class->draw;
 	org_key_pressed = wid_class->key_pressed;
+	org_size_request = wid_class->size_request;
 
 	wid_class->destroy = gnt_menu_destroy;
 	wid_class->draw = gnt_menu_draw;
@@ -327,9 +423,11 @@
 		widget->priv.y = 0;
 	} else {
 		GNT_TREE(widget)->show_separator = FALSE;
-		_gnt_tree_init_internals(GNT_TREE(widget), 2);
-		gnt_tree_set_col_width(GNT_TREE(widget), 1, 1);  /* The second column is to indicate that it has a submenu */
-		gnt_tree_set_column_resizable(GNT_TREE(widget), 1, FALSE);
+		_gnt_tree_init_internals(GNT_TREE(widget), NUM_COLUMNS);
+		gnt_tree_set_col_width(GNT_TREE(widget), ITEM_TRIGGER, 3);
+		gnt_tree_set_column_resizable(GNT_TREE(widget), ITEM_TRIGGER, FALSE);
+		gnt_tree_set_col_width(GNT_TREE(widget), ITEM_SUBMENU, 1);
+		gnt_tree_set_column_resizable(GNT_TREE(widget), ITEM_SUBMENU, FALSE);
 		GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER);
 	}
 
--- a/finch/libgnt/gntmenuitem.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntmenuitem.c	Sun Jul 08 04:27:02 2007 +0000
@@ -104,3 +104,13 @@
 	item->submenu = menu;
 }
 
+void gnt_menuitem_set_trigger(GntMenuItem *item, char trigger)
+{
+	item->priv.trigger = trigger;
+}
+
+char gnt_menuitem_get_trigger(GntMenuItem *item)
+{
+	return item->priv.trigger;
+}
+
--- a/finch/libgnt/gntmenuitem.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntmenuitem.h	Sun Jul 08 04:27:02 2007 +0000
@@ -52,6 +52,7 @@
 	/* These will be used to determine the position of the submenu */
 	int x;
 	int y;
+	char trigger;
 };
 
 typedef void (*GntMenuItemCallback)(GntMenuItem *item, gpointer data);
@@ -114,6 +115,25 @@
  */
 void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu);
 
+/**
+ * Set a trigger key for the item.
+ *
+ * @param item     The menuitem
+ * @param trigger  The key that will trigger the item when the parent manu is visible
+ */
+void gnt_menuitem_set_trigger(GntMenuItem *item, char trigger);
+
+/**
+ * Get the trigger key for a menuitem.
+ *
+ * @param item   The menuitem
+ *
+ * @return The trigger key for the menuitem.
+ *
+ * @see gnt_menuitem_set_trigger
+ */
+char gnt_menuitem_get_trigger(GntMenuItem *item);
+
 G_END_DECLS
 
 #endif /* GNT_MENUITEM_H */
--- a/finch/libgnt/gnttextview.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Sun Jul 08 04:27:02 2007 +0000
@@ -20,9 +20,11 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "gntstyle.h"
 #include "gnttextview.h"
 #include "gntutils.h"
 
+#include <stdlib.h>
 #include <string.h>
 
 enum
@@ -793,3 +795,43 @@
 	view->flags |= flag;
 }
 
+static gboolean
+check_for_pager_cb(GntWidget *widget, const char *key, GntTextView *view)
+{
+	static const char *combin = NULL;
+	char *argv[] = {NULL, NULL, NULL};
+	static char path[1024];
+	static int len = -1;
+	FILE *file;
+
+	if (combin == NULL) {
+		combin = gnt_key_translate(gnt_style_get_from_name("pager", "key"));
+		if (combin == NULL)
+			combin = "\033" "v";
+		len = g_snprintf(path, sizeof(path), "%s" G_DIR_SEPARATOR_S "gnt", g_get_tmp_dir());
+	} else {
+		g_snprintf(path + len, sizeof(path) - len, "XXXXXX");
+	}
+
+	if (strcmp(key, combin)) {
+		return FALSE;
+	}
+
+	file = fdopen(g_mkstemp(path), "wb");
+	if (!file)
+		return FALSE;
+
+	fprintf(file, "%s", view->string->str);
+	fclose(file);
+	argv[0] = gnt_style_get_from_name("pager", "path");
+	argv[0] = argv[0] ? argv[0] : getenv("PAGER");
+	argv[0] = argv[0] ? argv[0] : "less";
+	argv[1] = path;
+	return gnt_giveup_console(NULL, argv, NULL, NULL, NULL, NULL);
+}
+
+void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager)
+{
+	g_signal_connect(pager, "key_pressed", G_CALLBACK(check_for_pager_cb), view);
+}
+
--- a/finch/libgnt/gnttextview.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gnttextview.h	Sun Jul 08 04:27:02 2007 +0000
@@ -184,6 +184,13 @@
 void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget);
 
 /**
+ * 
+ * @param view
+ * @param widget
+ */
+void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager);
+
+/**
  * Set a GntTextViewFlag for the textview widget.
  *
  * @param view  The textview widget
--- a/finch/libgnt/gnttree.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gnttree.c	Sun Jul 08 04:27:02 2007 +0000
@@ -32,6 +32,8 @@
 #define SEARCHING(tree)  (tree->search && tree->search->len > 0)
 
 #define COLUMN_INVISIBLE(tree, index)  (tree->columns[index].flags & GNT_TREE_COLUMN_INVISIBLE)
+#define BINARY_DATA(tree, index)       (tree->columns[index].flags & GNT_TREE_COLUMN_BINARY_DATA)
+#define RIGHT_ALIGNED(tree, index)       (tree->columns[index].flags & GNT_TREE_COLUMN_RIGHT_ALIGNED)
 
 enum
 {
@@ -69,6 +71,7 @@
 struct _GntTreeCol
 {
 	char *text;
+	gboolean isbinary;
 	int span;       /* How many columns does it span? */
 };
 
@@ -84,13 +87,15 @@
 	int width;
 #define WIDTH(i) (tree->columns[i].width_ratio ? tree->columns[i].width_ratio : tree->columns[i].width)
 	gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
+	if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER))
+		width -= 2;
 	for (i = 0, total = 0; i < tree->ncol ; i++) {
 		if (tree->columns[i].flags & GNT_TREE_COLUMN_INVISIBLE)
 			continue;
 		if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE)
-			width -= WIDTH(i);
+			width -= WIDTH(i) + 1;
 		else
-			total += WIDTH(i);
+			total += WIDTH(i) + 1;
 	}
 
 	if (total == 0)
@@ -136,6 +141,8 @@
 {
 	GntTree *t = row->tree;
 	if (t->search && t->search->len > 0) {
+		/* XXX: Allow setting the search column. And make sure the search column
+		 * doesn't contain binary data. */
 		char *one = g_utf8_casefold(((GntTreeCol*)row->columns->data)->text, -1);
 		char *two = g_utf8_casefold(t->search->str, -1);
 		char *z = strstr(one, two);
@@ -279,27 +286,28 @@
 	GList *iter;
 	int i;
 	gboolean notfirst = FALSE;
-	int lastvisible = tree->ncol;
-
-	while (lastvisible && COLUMN_INVISIBLE(tree, lastvisible))
-		lastvisible--;
 
 	for (i = 0, iter = row->columns; i < tree->ncol && iter; i++, iter = iter->next)
 	{
 		GntTreeCol *col = iter->data;
 		const char *text;
-		int len = gnt_util_onscreen_width(col->text, NULL);
+		int len;
 		int fl = 0;
 		gboolean cut = FALSE;
 		int width;
+		const char *display;
 
 		if (COLUMN_INVISIBLE(tree, i))
 			continue;
 
-		if (i == lastvisible)
-			width = GNT_WIDGET(tree)->priv.width - gnt_util_onscreen_width(string->str, NULL);
+		if (BINARY_DATA(tree, i))
+			display = "";
 		else
-			width = tree->columns[i].width;
+			display = col->text;
+
+		len = gnt_util_onscreen_width(display, NULL);
+
+		width = tree->columns[i].width;
 
 		if (i == 0)
 		{
@@ -327,8 +335,7 @@
 				g_string_append_printf(string, "%*s", fl, "");
 			}
 			len += fl;
-		}
-		else if (notfirst)
+		} else if (notfirst && tree->show_separator)
 			g_string_append_c(string, '|');
 		else
 			g_string_append_c(string, ' ');
@@ -339,8 +346,13 @@
 			len = width - 1;
 			cut = TRUE;
 		}
-		text = gnt_util_onscreen_width_to_pointer(col->text, len - fl, NULL);
-		string = g_string_append_len(string, col->text, text - col->text);
+
+		if (RIGHT_ALIGNED(tree, i) && len < tree->columns[i].width) {
+			g_string_append_printf(string, "%*s", width - len, "");
+		}
+
+		text = gnt_util_onscreen_width_to_pointer(display, len - fl, NULL);
+		string = g_string_append_len(string, display, text - display);
 		if (cut) { /* ellipsis */
 			if (gnt_ascii_only())
 				g_string_append_c(string, '~');
@@ -349,7 +361,7 @@
 			len++;
 		}
 
-		if (len < tree->columns[i].width && iter->next)
+		if (!RIGHT_ALIGNED(tree, i) && len < tree->columns[i].width && iter->next)
 			g_string_append_printf(string, "%*s", width - len, "");
 	}
 	return g_string_free(string, FALSE);
@@ -586,9 +598,13 @@
 	{
 		GntTree *tree = GNT_TREE(widget);
 		int i, width = 0;
+		width = 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER));
 		for (i = 0; i < tree->ncol; i++)
-			if (!COLUMN_INVISIBLE(tree, i))
-				width += tree->columns[i].width + 1;
+			if (!COLUMN_INVISIBLE(tree, i)) {
+				width = width + tree->columns[i].width;
+				if (tree->lastvisible != i)
+					width++;
+			}
 		widget->priv.width = width;
 	}
 }
@@ -1003,8 +1019,8 @@
 free_tree_col(gpointer data)
 {
 	GntTreeCol *col = data;
-
-	g_free(col->text);
+	if (col->isbinary)
+		g_free(col->text);
 	g_free(col);
 }
 
@@ -1390,8 +1406,12 @@
 	if (row)
 	{
 		col = g_list_nth_data(row->columns, colno);
-		g_free(col->text);
-		col->text = g_strdup(text ? text : "");
+		if (BINARY_DATA(tree, colno)) {
+			col->text = (gpointer)text;
+		} else {
+			g_free(col->text);
+			col->text = g_strdup(text ? text : "");
+		}
 
 		if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) >= 0)
 			redraw_tree(tree);
@@ -1486,6 +1506,7 @@
 	tree->ncol = col;
 	tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
 	tree->columns = g_new0(struct _GntTreeColInfo, col);
+	tree->lastvisible = col - 1;
 	while (col--)
 	{
 		tree->columns[col].width = 15;
@@ -1517,7 +1538,13 @@
 	{
 		GntTreeCol *col = g_new0(GntTreeCol, 1);
 		col->span = 1;
-		col->text = g_strdup(iter->data ? iter->data : "");
+		if (BINARY_DATA(tree, i)) {
+			col->text = iter->data;
+			col->isbinary = TRUE;
+		} else {
+			col->text = g_strdup(iter->data ? iter->data : "");
+			col->isbinary = FALSE;
+		}
 
 		row->columns = g_list_append(row->columns, col);
 	}
@@ -1625,9 +1652,14 @@
 
 	twidth = 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER));
 	for (i = 0; i < tree->ncol; i++) {
+		if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE)
+			widths[i] = tree->columns[i].width;
 		gnt_tree_set_col_width(tree, i, widths[i]);
-		if (!COLUMN_INVISIBLE(tree, i))
-			twidth += widths[i] + (tree->show_separator ? 1 : 0) + 1;
+		if (!COLUMN_INVISIBLE(tree, i)) {
+			twidth = twidth + widths[i];
+			if (tree->lastvisible != i)
+				twidth += 1;
+		}
 	}
 	g_free(widths);
 
@@ -1654,6 +1686,18 @@
 {
 	g_return_if_fail(col < tree->ncol);
 	set_column_flag(tree, col, GNT_TREE_COLUMN_INVISIBLE, !vis);
+	if (vis) {
+		/* the column is visible */
+		if (tree->lastvisible < col)
+			tree->lastvisible = col;
+	} else {
+		if (tree->lastvisible == col)
+			while (tree->lastvisible) {
+				tree->lastvisible--;
+				if (!COLUMN_INVISIBLE(tree, tree->lastvisible))
+					break;
+			}
+	}
 }
 
 void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res)
@@ -1662,6 +1706,18 @@
 	set_column_flag(tree, col, GNT_TREE_COLUMN_FIXED_SIZE, !res);
 }
 
+void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin)
+{
+	g_return_if_fail(col < tree->ncol);
+	set_column_flag(tree, col, GNT_TREE_COLUMN_FIXED_SIZE, bin);
+}
+
+void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right)
+{
+	g_return_if_fail(col < tree->ncol);
+	set_column_flag(tree, col, GNT_TREE_COLUMN_RIGHT_ALIGNED, right);
+}
+
 void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[])
 {
 	int i;
--- a/finch/libgnt/gnttree.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gnttree.h	Sun Jul 08 04:27:02 2007 +0000
@@ -50,6 +50,8 @@
 typedef enum {
 	GNT_TREE_COLUMN_INVISIBLE    = 1 << 0,
 	GNT_TREE_COLUMN_FIXED_SIZE   = 1 << 1,
+	GNT_TREE_COLUMN_BINARY_DATA  = 1 << 2,
+	GNT_TREE_COLUMN_RIGHT_ALIGNED = 1 << 3,
 } GntTreeColumnFlag;
 
 struct _GntTree
@@ -85,6 +87,7 @@
 	int search_timeout;
 
 	GCompareFunc compare;
+	int lastvisible;
 };
 
 struct _GntTreeClass
@@ -103,203 +106,253 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * @return The GType for GntTree
  */
 GType gnt_tree_get_gtype(void);
 
 /**
- * 
+ * Create a tree with one column.
  *
- * @return
+ * @return The newly created tree
+ *
+ * @see gnt_tree_new_with_columns
  */
 GntWidget * gnt_tree_new(void);
 
-      /* A tree with just one column */
-
 /**
- * 
- * @param columns
+ * Create a tree with a specified number of columns.
+ *
+ * @param columns  Number of columns
  *
- * @return
+ * @return  The newly created tree
+ *
+ * @see gnt_tree_new
  */
 GntWidget * gnt_tree_new_with_columns(int columns);
 
 /**
- * 
- * @param tree
- * @param rows
+ * The number of rows the tree should display at a time.
+ *
+ * @param tree  The tree
+ * @param rows  The number of rows
  */
 void gnt_tree_set_visible_rows(GntTree *tree, int rows);
 
 /**
- * 
- * @param tree
+ * Get the number visible rows.
  *
- * @return
+ * @param tree  The tree
+ *
+ * @return  The number of visible rows
  */
 int gnt_tree_get_visible_rows(GntTree *tree);
 
 /**
- * 
- * @param tree
- * @param count
+ * Scroll the contents of the tree.
+ *
+ * @param tree   The tree
+ * @param count  If positive, the tree will be scrolled down by count rows,
+ *               otherwise, it will be scrolled up by count rows.
  */
 void gnt_tree_scroll(GntTree *tree, int count);
 
 /**
- * 
- * @param tree
- * @param key
- * @param row
- * @param parent
- * @param bigbro
+ * Insert a row in the tree.
  *
- * @return
+ * @param tree    The tree
+ * @param key     The key for the row
+ * @param row     The row to insert
+ * @param parent  The key for the parent row
+ * @param bigbro  The key for the row to insert the new row after.
+ *
+ * @return  The inserted row
+ *
+ * @see gnt_tree_create_row
+ * @see gnt_tree_add_row_last
+ * @see gnt_tree_add_choice
  */
 GntTreeRow * gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
 
 /**
- * 
- * @param tree
- * @param key
- * @param row
- * @param parent
+ * Insert a row at the end of the tree.
  *
- * @return
+ * @param tree    The tree
+ * @param key     The key for the row
+ * @param row     The row to insert
+ * @param parent  The key for the parent row
+ *
+ * @return The inserted row
+ *
+ * @see gnt_tree_create_row
+ * @see gnt_tree_add_row_after
+ * @see gnt_tree_add_choice
  */
 GntTreeRow * gnt_tree_add_row_last(GntTree *tree, void *key, GntTreeRow *row, void *parent);
 
 /**
- * 
- * @param tree
+ * Get the key for the selected row.
  *
- * @return
+ * @param tree  The tree
+ *
+ * @return   The key for the selected row
  */
 gpointer gnt_tree_get_selection_data(GntTree *tree);
 
-/* Returned string needs to be freed */
 /**
- * 
- * @param tree
+ * Get the text displayed for the selected row.
  *
- * @return
+ * @param tree  The tree
+ *
+ * @return  The text, which needs to be freed by the caller
  */
 char * gnt_tree_get_selection_text(GntTree *tree);
 
 /**
- * 
- * @param tree
+ * Get a list of text of the current row.
  *
- * @return
+ * @param tree  The tree
+ *
+ * @return A list of texts of the currently selected row. The list
+ *         and its data should be freed by the caller.
  */
 GList * gnt_tree_get_selection_text_list(GntTree *tree);
 
 /**
+ * Returns the list of rows in the tree.
  *
- * @param tree
+ * @param tree  The tree
  *
- * @constreturn
+ * @return The list of the rows. The list should not be modified by the caller.
  */
 GList *gnt_tree_get_rows(GntTree *tree);
 
 /**
- * 
- * @param tree
- * @param key
+ * Remove a row from the tree.
+ *
+ * @param tree  The tree
+ * @param key   The key for the row to remove
  */
 void gnt_tree_remove(GntTree *tree, gpointer key);
 
 /**
- * 
- * @param tree
+ * Remove all the item from the tree.
+ *
+ * @param tree  The tree
  */
 void gnt_tree_remove_all(GntTree *tree);
 
-/* Returns the visible line number of the selected row */
 /**
- * 
- * @param tree
+ * Get the visible line number of the selected row.
  *
- * @return
+ * @param tree  The tree
+ *
+ * @return  The line number of the currently selected row
  */
 int gnt_tree_get_selection_visible_line(GntTree *tree);
 
 /**
- * 
- * @param tree
- * @param key
- * @param colno
- * @param text
+ * Change the text of a column in a row.
+ *
+ * @param tree   The tree
+ * @param key    The key for the row
+ * @param colno  The index of the column
+ * @param text   The new text
  */
 void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text);
 
 /**
- * 
- * @param tree
- * @param key
- * @param row
- * @param parent
- * @param bigbro
+ * Add a checkable item in the tree.
  *
- * @return
+ * @param tree    The tree
+ * @param key     The key for the row
+ * @param row     The row to add
+ * @param parent  The parent of the row, or @c NULL
+ * @param bigbro  The row to insert after, or @c NULL
+ *
+ * @return  The row inserted.
+ *
+ * @see gnt_tree_create_row
+ * @see gnt_tree_create_row_from_list
+ * @see gnt_tree_add_row_last
+ * @see gnt_tree_add_row_after
  */
 GntTreeRow * gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
 
 /**
- * 
- * @param tree
- * @param key
- * @param set
+ * Set whether a checkable item is checked or not.
+ *
+ * @param tree   The tree
+ * @param key    The key for the row
+ * @param set    @c TRUE if the item should be checked, @c FALSE if not
  */
 void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set);
 
 /**
- * 
- * @param tree
- * @param key
+ * Return whether a row is selected or not, where the row is a checkable item.
  *
- * @return
+ * @param tree  The tree
+ * @param key   The key for the row
+ *
+ * @return    @c TRUE if the row is checked, @c FALSE otherwise.
  */
 gboolean gnt_tree_get_choice(GntTree *tree, void *key);
 
 /**
- * 
- * @param tree
- * @param key
- * @param flags
+ * Set flags for the text in a row in the tree.
+ *
+ * @param tree   The tree
+ * @param key    The key for the row
+ * @param flags  The flags to set
  */
 void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags);
 
 /**
- * 
- * @param key
+ * Select a row.
+ *
+ * @param tree  The tree
+ * @param key   The key of the row to select
  */
 void gnt_tree_set_selected(GntTree *tree , void *key);
 
 /**
- * 
- * @param tree
+ * Create a row to insert in the tree.
+ *
+ * @param tree The tree
+ * @param ...  A string for each column in the tree
  *
- * @return
+ * @return   The row
+ *
+ * @see gnt_tree_create_row_from_list
+ * @see gnt_tree_add_row_after
+ * @see gnt_tree_add_row_last
+ * @see gnt_tree_add_choice
  */
 GntTreeRow * gnt_tree_create_row(GntTree *tree, ...);
 
 /**
- * 
- * @param tree
- * @param list
+ * Create a row from a list of text.
+ *
+ * @param tree  The tree
+ * @param list  The list containing the text for each column
  *
- * @return
+ * @return   The row
+ *
+ * @see gnt_tree_create_row
+ * @see gnt_tree_add_row_after
+ * @see gnt_tree_add_row_last
+ * @see gnt_tree_add_choice
  */
 GntTreeRow * gnt_tree_create_row_from_list(GntTree *tree, GList *list);
 
 /**
- * 
- * @param tree
- * @param col
- * @param width
+ * Set the width of a column in the tree.
+ *
+ * @param tree   The tree
+ * @param col    The index of the column
+ * @param width  The width for the column
+ *
+ * @see gnt_tree_set_column_width_ratio
+ * @see gnt_tree_set_column_resizable
  */
 void gnt_tree_set_col_width(GntTree *tree, int col, int width);
 
@@ -309,64 +362,87 @@
  * @param tree   The tree
  * @param index  The index of the column
  * @param title  The title for the column
+ *
+ * @see gnt_tree_set_column_titles
+ * @see gnt_tree_set_show_title
  */
 void gnt_tree_set_column_title(GntTree *tree, int index, const char *title);
 
 /**
- * 
- * @param tree
+ * Set the titles of the columns
+ *
+ * @param tree  The tree
+ * @param ...   One title for each column in the tree
+ *
+ * @see gnt_tree_set_column_title
+ * @see gnt_tree_set_show_title
  */
 void gnt_tree_set_column_titles(GntTree *tree, ...);
 
 /**
- * 
- * @param tree
- * @param set
+ * Set whether to display the title of the columns.
+ *
+ * @param tree  The tree
+ * @param set   If @c TRUE, the column titles are displayed
+ *
+ * @see gnt_tree_set_column_title
+ * @see gnt_tree_set_column_titles
  */
 void gnt_tree_set_show_title(GntTree *tree, gboolean set);
 
 /**
- * 
- * @param tree
- * @param func
+ * Set the compare function for sorting the data.
+ *
+ * @param tree  The tree
+ * @param func  The comparison function, which is used to compare
+ *              the keys
+ *
+ * @see gnt_tree_sort_row 
  */
 void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func);
 
 /**
- * 
- * @param tree
- * @param key
- * @param expanded
+ * Set whether a row, which has child rows, should be expanded.
+ *
+ * @param tree      The tree
+ * @param key       The key of the row
+ * @param expanded  Whether to expand the child rows
  */
 void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded);
 
 /**
- * 
- * @param tree
- * @param set
+ * Set whether to show column separators.
+ *
+ * @param tree  The tree
+ * @param set   If @c TRUE, the column separators are displayed
  */
 void gnt_tree_set_show_separator(GntTree *tree, gboolean set);
 
 /**
- * 
- * @param tree
- * @param row
+ * Sort a row in the tree.
+ *
+ * @param tree  The tree
+ * @param row   The row to sort
+ *
+ * @see gnt_tree_set_compare_func
  */
 void gnt_tree_sort_row(GntTree *tree, void *row);
 
-/* This will try to automatically adjust the width of the columns in the tree */
 /**
- * 
- * @param tree
+ * Automatically adjust the width of the columns in the tree.
+ *
+ * @param tree  The tree
  */
 void gnt_tree_adjust_columns(GntTree *tree);
 
 /**
- * 
- * @param tree
- * @param hash
- * @param eq
- * @param kd
+ * Set the hash functions to use to hash, compare and free the keys.
+ *
+ * @param tree  The tree
+ * @param hash  The hashing function
+ * @param eq    The function to compare keys
+ * @param kd    The function to use to free the keys when a row is removed
+ *              from the tree
  */
 void gnt_tree_set_hash_fns(GntTree *tree, gpointer hash, gpointer eq, gpointer kd);
 
@@ -389,10 +465,32 @@
  * @param col   The index of the column
  * @param res   If @c FALSE, the column will not be resized when the
  *              tree is resized
+ *
+ * @see gnt_tree_set_col_width
+ * @see gnt_tree_set_column_width_ratio
  */
 void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res);
 
 /**
+ * Set whether data in a column should be considered as binary data, and
+ * not as strings. A column containing binary data will be display empty text.
+ *
+ * @param tree  The tree
+ * @param col   The index of the column
+ * @param bin   @c TRUE if the data for the column is binary
+ */
+void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin);
+
+/**
+ * Set whether text in a column should be right-aligned.
+ *
+ * @param tree  The tree
+ * @param col   The index of the column
+ * @param right @c TRUE if the text in the column should be right aligned
+ */
+void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right);
+
+/**
  * Set column widths to use when calculating column widths after a tree
  * is resized.
  *
@@ -400,6 +498,9 @@
  * @param cols   Array of widths. The width must have the same number
  *               of entries as the number of columns in the tree, or
  *               end with a negative value for a column-width.
+ *
+ * @see gnt_tree_set_col_width
+ * @see gnt_tree_set_column_resizable
  */
 void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]);
 
@@ -408,11 +509,6 @@
 /* The following functions should NOT be used by applications. */
 
 /* This should be called by the subclasses of GntTree's in their _new function */
-/**
- * 
- * @param tree
- * @param col
- */
 void _gnt_tree_init_internals(GntTree *tree, int col);
 
 #endif /* GNT_TREE_H */
--- a/finch/libgnt/gntwm.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntwm.c	Sun Jul 08 04:27:02 2007 +0000
@@ -204,6 +204,9 @@
 static gboolean
 update_screen(GntWM *wm)
 {
+	if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD)
+		return TRUE;
+
 	if (wm->menu) {
 		GntMenu *top = wm->menu;
 		while (top) {
@@ -377,13 +380,7 @@
 	else if (pos >= 0)
 		wid = g_list_nth_data(wm->cws->list, pos);
 
-	wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, wid);
-
-	gnt_wm_raise_window(wm, wm->cws->ordered->data);
-
-	if (w != wid) {
-		gnt_widget_set_focus(w, FALSE);
-	}
+	gnt_wm_raise_window(wm, wid);
 }
 
 static gboolean
@@ -406,7 +403,6 @@
 switch_window_n(GntBindable *bind, GList *list)
 {
 	GntWM *wm = GNT_WM(bind);
-	GntWidget *w = NULL;
 	GList *l;
 	int n;
 
@@ -418,17 +414,11 @@
 	else
 		n = 0;
 
-	w = wm->cws->ordered->data;
-
 	if ((l = g_list_nth(wm->cws->list, n)) != NULL)
 	{
 		gnt_wm_raise_window(wm, l->data);
 	}
 
-	if (l && w != l->data)
-	{
-		gnt_widget_set_focus(w, FALSE);
-	}
 	return TRUE;
 }
 
@@ -1038,7 +1028,7 @@
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
 	curs_set(0);   /* endwin resets the cursor to normal */
 
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean
@@ -1362,7 +1352,6 @@
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
 	update_screen(wm);
 	if (wm->cws->ordered) {
-		gnt_widget_set_focus(wm->cws->ordered->data, TRUE);
 		gnt_wm_raise_window(wm, wm->cws->ordered->data);
 	}
 
@@ -1573,29 +1562,16 @@
 	if (!transient) {
 		GntWS *ws = wm->cws;
 		if (node->me != wm->_list.window) {
-			GntWidget *w = NULL;
-
 			if (GNT_IS_BOX(widget)) {
 				ws = new_widget_find_workspace(wm, widget);
 			}
-
-			if (ws->ordered)
-				w = ws->ordered->data;
-
 			node->ws = ws;
 			ws->list = g_list_append(ws->list, widget);
-
-			if (wm->event_stack)
-				ws->ordered = g_list_prepend(ws->ordered, widget);
-			else
-				ws->ordered = g_list_append(ws->ordered, widget);
-
-			gnt_widget_set_focus(widget, TRUE);
-			if (w)
-				gnt_widget_set_focus(w, FALSE);
+			ws->ordered = g_list_append(ws->ordered, widget);
 		}
 
-		if (wm->event_stack || node->me == wm->_list.window) {
+		if (wm->event_stack || node->me == wm->_list.window ||
+				node->me == ws->ordered->data) {
 			gnt_wm_raise_window(wm, node->me);
 		} else {
 			bottom_panel(node->panel);     /* New windows should not grab focus */
@@ -1976,6 +1952,14 @@
 	GntWS *ws = gnt_wm_widget_find_workspace(wm, widget);
 	if (wm->cws != ws)
 		gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
+	if (widget != wm->cws->ordered->data) {
+		GntWidget *wid = wm->cws->ordered->data;
+		wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
+		gnt_widget_set_focus(wid, FALSE);
+		gnt_widget_draw(wid);
+	}
+	gnt_widget_set_focus(widget, TRUE);
+	gnt_widget_draw(widget);
 	g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget);
 }
 
--- a/finch/libgnt/gntwm.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntwm.h	Sun Jul 08 04:27:02 2007 +0000
@@ -46,6 +46,7 @@
 	GNT_KP_MODE_NORMAL,
 	GNT_KP_MODE_RESIZE,
 	GNT_KP_MODE_MOVE,
+	GNT_KP_MODE_WAIT_ON_CHILD
 } GntKeyPressMode;
 
 typedef struct
--- a/finch/libgnt/gntws.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/gntws.c	Sun Jul 08 04:27:02 2007 +0000
@@ -35,6 +35,9 @@
 	int n, width = 0;
 	int i;
 
+	if (gnt_is_refugee())
+		return;
+
 	if (taskbar == NULL) {
 		taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0);
 	} else if (reposition) {
@@ -114,11 +117,13 @@
 	g_list_foreach(ws->ordered, widget_hide, nodes);
 }
 
-void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes) {
+void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes)
+{
 	widget_hide(widget, nodes);
 }
 
-void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes) {
+void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes)
+{
 	widget_show(widget, nodes);
 }
 
--- a/finch/libgnt/wms/irssi.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/finch/libgnt/wms/irssi.c	Sun Jul 08 04:27:02 2007 +0000
@@ -181,8 +181,10 @@
 			GNT_BOX(node->me)->title);
 	wbkgdset(node->window, '\0' | COLOR_PAIR(gnt_widget_has_focus(node->me) ? GNT_COLOR_TITLE : GNT_COLOR_TITLE_D));
 	mvwaddstr(node->window, 0, 0, title);
-	update_panels();
-	doupdate();
+	if (!gnt_is_refugee()) {
+		update_panels();
+		doupdate();
+	}
 	return FALSE;
 }
 
--- a/libpurple/account.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/account.c	Sun Jul 08 04:27:02 2007 +0000
@@ -2183,6 +2183,7 @@
 purple_accounts_delete(PurpleAccount *account)
 {
 	PurpleBlistNode *gnode, *cnode, *bnode;
+	GList *iter;
 
 	g_return_if_fail(account != NULL);
 
@@ -2231,6 +2232,14 @@
 		}
 	}
 
+	/* Remove any open conversation for this account */
+	for (iter = purple_get_conversations(); iter; ) {
+		PurpleConversation *conv = iter->data;
+		iter = iter->next;
+		if (purple_conversation_get_account(conv) == account)
+			purple_conversation_destroy(conv);
+	}
+
 	/* Remove this account's pounces */
 	purple_pounce_destroy_all_by_account(account);
 
--- a/libpurple/internal.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/internal.h	Sun Jul 08 04:27:02 2007 +0000
@@ -93,8 +93,9 @@
 #include <langinfo.h>
 #endif
 
+#include <gmodule.h>
+
 #ifdef PURPLE_PLUGINS
-# include <gmodule.h>
 # ifndef _WIN32
 #  include <dlfcn.h>
 # endif
--- a/libpurple/plugins/autoaccept.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/plugins/autoaccept.c	Sun Jul 08 04:27:02 2007 +0000
@@ -178,7 +178,8 @@
 {
 	PurpleMenuAction *action;
 
-	if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node))
+	if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node) &&
+		!(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE))
 		return;
 
 	action = purple_menu_action_new(_("Autoaccept File Transfers..."),
--- a/libpurple/plugins/perl/common/Makefile.mingw	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/plugins/perl/common/Makefile.mingw	Sun Jul 08 04:27:02 2007 +0000
@@ -20,6 +20,7 @@
 INCLUDE_PATHS +=		-I. \
 			-I$(PIDGIN_TREE_TOP) \
 			-I$(PURPLE_TOP) \
+			-I$(PURPLE_TOP)/win32 \
 			-I$(GTK_TOP)/include \
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/lib/glib-2.0/include \
--- a/libpurple/plugins/perl/perl-common.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/plugins/perl/perl-common.h	Sun Jul 08 04:27:02 2007 +0000
@@ -11,7 +11,14 @@
 
 /* XXX: perl defines it's own _ but I think it's safe to undef it */
 #undef _
+/* Dirty hack to prevent the win32 libc compat stuff from interfering with the Perl internal stuff */
+#ifdef _WIN32
+#define _WIN32DEP_H_
+#endif
 #include "internal.h"
+#ifdef _WIN32
+#undef _WIN32DEP_H_
+#endif
 #include "plugin.h"
 #include "value.h"
 
--- a/libpurple/protocols/gg/gg.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/protocols/gg/gg.c	Sun Jul 08 04:27:02 2007 +0000
@@ -1074,9 +1074,7 @@
 		}
 	}
 
-	val = ggp_buddy_get_name(gc, ggp_str_to_uin(who));
-	purple_notify_userinfo(gc, val, user_info, ggp_sr_close_cb, form);
-	g_free(val);
+	purple_notify_userinfo(gc, who, user_info, ggp_sr_close_cb, form);
 	g_free(who);
 	purple_notify_user_info_destroy(user_info);
 }
--- a/libpurple/protocols/msn/directconn.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sun Jul 08 04:27:02 2007 +0000
@@ -370,7 +370,7 @@
 	else
 	{
 		struct sockaddr_in client_addr;
-		unsigned int client;
+		socklen_t client;
 		fd = accept (source, (struct sockaddr *)&client_addr, &client);
 	}
 
--- a/libpurple/win32/win32dep.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/libpurple/win32/win32dep.c	Sun Jul 08 04:27:02 2007 +0000
@@ -43,8 +43,6 @@
 /*
  *  DEFINES & MACROS
  */
-#define _(x) gettext(x)
-
 #define WIN32_PROXY_REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
 
 /* For shfolder.dll */
--- a/pidgin/gtkblist.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/gtkblist.c	Sun Jul 08 04:27:02 2007 +0000
@@ -3417,13 +3417,16 @@
 			time_t idle_secs = purple_presence_get_idle_time(presence);
 
 			if (idle_secs > 0) {
-				int ihrs, imin;
+				int iday, ihrs, imin;
 
 				time(&t);
-				ihrs = (t - idle_secs) / 3600;
+				iday = (t - idle_secs) / (24 * 60 * 60);
+				ihrs = ((t - idle_secs) / 60 / 60) % 24;
 				imin = ((t - idle_secs) / 60) % 60;
 
-				if (ihrs)
+                if (iday)
+					idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin);
+				else if (ihrs)
 					idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin);
 				else
 					idletime = g_strdup_printf(_("Idle %dm"), imin);
--- a/pidgin/gtkconv.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Jul 08 04:27:02 2007 +0000
@@ -4857,8 +4857,14 @@
 
 	gtkconv->convs = g_list_remove(gtkconv->convs, conv);
 	/* Don't destroy ourselves until all our convos are gone */
-	if (gtkconv->convs)
+	if (gtkconv->convs) {
+		/* Make sure the destroyed conversation is not the active one */
+		if (gtkconv->active_conv == conv) {
+			gtkconv->active_conv = gtkconv->convs->data;
+			purple_conversation_update(gtkconv->active_conv, PURPLE_CONV_UPDATE_FEATURES);
+		}
 		return;
+	}
 
 	pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
 
--- a/pidgin/gtkimhtml.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/gtkimhtml.c	Sun Jul 08 04:27:02 2007 +0000
@@ -28,8 +28,8 @@
 #include <config.h>
 #endif
 
+#include "internal.h"
 #include "pidgin.h"
-#include "internal.h"
 
 #include "debug.h"
 #include "util.h"
--- a/pidgin/plugins/perl/common/Makefile.mingw	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/plugins/perl/common/Makefile.mingw	Sun Jul 08 04:27:02 2007 +0000
@@ -18,6 +18,7 @@
 INCLUDE_PATHS =		-I. \
 			-I$(PIDGIN_TREE_TOP) \
 			-I$(PURPLE_TOP) \
+			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TOP) \
 			-I$(PIDGIN_TOP)/win32 \
 			-I$(GTK_TOP)/include \
--- a/pidgin/win32/gtkwin32dep.c	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Sun Jul 08 04:27:02 2007 +0000
@@ -36,6 +36,8 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkwin32.h>
 
+#include "internal.h"
+
 #include "debug.h"
 #include "notify.h"
 
--- a/pidgin/win32/gtkwin32dep.h	Sun Jul 08 04:23:55 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.h	Sun Jul 08 04:27:02 2007 +0000
@@ -45,5 +45,5 @@
 void winpidgin_post_init(void);
 void winpidgin_cleanup(void);
 
-#endif /* _WIN32DEP_H_ */
+#endif /* _GTKWIN32DEP_H_ */