changeset 20461:25542d5c94ed

propagate from branch 'im.pidgin.pidgin' (head 007693114b23cd2711c1cb9030a148e2f63de8c8) to branch 'im.pidgin.cpw.khc.msnp14' (head 46048b0e7414ad1a4545b3a49494fea80191a9d1)
author Ka-Hing Cheung <khc@hxbc.us>
date Sat, 07 Jul 2007 03:52:12 +0000
parents 7122bcebf7c9 (diff) 8f7ccadce62d (current diff)
children 3537f82db107
files COPYRIGHT libpurple/internal.h libpurple/protocols/msn/directconn.c libpurple/protocols/msn/msn-utils.c libpurple/protocols/msn/msn-utils.h libpurple/protocols/msn/msnutils.c pidgin/gtkdocklet.c
diffstat 144 files changed, 3155 insertions(+), 998 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Jul 05 06:48:38 2007 +0000
+++ b/COPYRIGHT	Sat Jul 07 03:52:12 2007 +0000
@@ -325,6 +325,7 @@
 Joe Shaw
 Scott Shedden
 Dossy Shiobara
+Michael Shkutkov
 Ettore Simone
 John Silvestri
 Craig Slusher
@@ -357,6 +358,7 @@
 Cestonaro Thilo
 Will Thompson
 Douglas Thrift (douglaswth)
+Mark Tiefenbruck
 Andrew Tinney
 Jeffery To
 Warren Togami
--- a/ChangeLog	Thu Jul 05 06:48:38 2007 +0000
+++ b/ChangeLog	Sat Jul 07 03:52:12 2007 +0000
@@ -11,12 +11,26 @@
 	  notifications in chats
 	* With the HTML logger, images in conversations are now saved.
 	  NOTE: Saved images are not yet displayed when loading logs.
+	* Added support for QIP logs to the Log Reader plugin (Michael Shkutkov)
 
 	Pidgin:
 	* Ensure only one copy of Pidgin is running with a given configuration
 	  directory.  The net effect of this is that trying to start Pidgin a
 	  second time will raise the buddy list.  (Gabriel Schulhof)
 	* Undo capability in the conversation window
+	* The formatting toolbar has been reorganized to be more concise.
+	* 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)
+	* There's a new custom window manager, Irssi
+	* 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/ChangeLog.API	Thu Jul 05 06:48:38 2007 +0000
+++ b/ChangeLog.API	Sat Jul 07 03:52:12 2007 +0000
@@ -73,6 +73,7 @@
 		* pidgin_menu_position_func_helper
 		* pidgin_blist_get_name_markup, returns the buddy list markup
 		  text for a given buddy.
+		* pidgin_themes_remove_smiley_theme
 
 		Changed:
 		* pidgin_append_menu_action returns the menuitem added to the menu.
@@ -88,10 +89,28 @@
 		Added:
 		* finch_retrieve_user_info
 
-		Changed:
-		* gnt_tree_get_rows() now returns a GList* instead of a const
-		  GList*, as const is not very useful with GLists.  The
-		  returned value still must not be modified or freed.
+		libgnt:
+			Added:
+			* GntWS for workspaces
+			* gnt_tree_set_column_title
+			* GntSlider widget
+			* "completion" signal for GntEntry
+			* "terminal-refresh" signal for GntWM, with a corresponding entry
+			  in GntWMClass
+			* New flags for GntTextView to decide whether to word-wrap or show
+			  scrollbars (GntTextViewFlag) which can be set by
+			  gnt_text_view_set_flag
+			* gnt_style_get_from_name
+			* gnt_window_present
+			* gnt_tree_set_column_width_ratio
+			* gnt_tree_set_column_resizable
+
+			Changed:
+			* gnt_tree_get_rows() now returns a GList* instead of a const
+			  GList*, as const is not very useful with GLists.  The
+			  returned value still must not be modified or freed.
+			* Instead of keeping an 'invisible' item, the GntTreeColumns now
+			  maintain 'flags' with the appropriate flags set
 
 version 2.0.2 (6/14/2007):
 	Pidgin:
--- a/Doxyfile.in	Thu Jul 05 06:48:38 2007 +0000
+++ b/Doxyfile.in	Sat Jul 07 03:52:12 2007 +0000
@@ -432,8 +432,9 @@
 # with spaces.
 
 INPUT                  = libpurple \
-						 finch \
-						 pidgin \
+	                     finch \
+	                     finch/libgnt \
+	                     pidgin \
                          doc
 
 # If the value of the INPUT tag contains directories, you can use the 
--- a/doc/finch.1.in	Thu Jul 05 06:48:38 2007 +0000
+++ b/doc/finch.1.in	Sat Jul 07 03:52:12 2007 +0000
@@ -110,14 +110,26 @@
 Bring up the menu (if there is one) for a window. Note that currently only the
 buddylist has a menu.
 .TP
-.B Alt \+ Shift \+ .
+.B Alt \+ /
+Show a list of available key-bindings for the current widget in focus.
+.TP
+.B Alt \+ \>
 Switch to the next workspace
 .TP
-.B Alt \+ Shift \+ ,
+.B Alt \+ \<
 Switch to the previous workspace
 .TP
+.B Alt \+ t
+Tag (or untag) the current window
+.TP
+.B Alt \+ T
+Attached all the tag windows to the current workspace
+.TP
 .B Alt \+ s
 Show the workspace list
+.TP
+.B F9
+Create a new workspace and switch to it
 
 .SH FILES
 \fI~/.gntrc\fR: configuration file for gnt applications.
@@ -135,7 +147,9 @@
 .br
 # To use some custom window-manager
 .br
-wm = /usr/local/lib/purple/s.so
+wm = /usr/local/lib/gnt/s.so
+.br
+# There's also a custom window manager called irssi.so
 .br
 # Remember window-positions based on the titles (on by default)
 .br
@@ -308,6 +322,8 @@
 .br
 pagedown = page-down
 .br
+backspace = move-parent
+.br
 # Following is the default binding for the context-menu
 .br
 menu = context-menu
@@ -377,6 +393,16 @@
 .br
 a-l = refresh-screen
 .br
+a-s = workspace-list
+.br
+a-t = window-tag
+.br
+a-T = place-tagged
+.br
+a-C = toggle-clipboard
+.br
+a-/ = help-for-widget
+.br
 # The following action is still incomplete, and doesn't have a default binding
 .br
 # switch-window-n
@@ -387,8 +413,18 @@
 [GntS::binding]
 .br
 a-b = toggle-buddylist
+
+# For the irssi window manager
 .br
-a-C = toggle-clipboard
+[Irssi::binding]
+.br
+a-L = move-right
+.br
+a-H = move-left
+.br
+a-J = move-down
+.br
+a-K = move-up
 
 .SH Conversation Commands
 There are a few helpful commands in addition to the regular commands. You can
--- a/doc/notify-signals.dox	Thu Jul 05 06:48:38 2007 +0000
+++ b/doc/notify-signals.dox	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,4 @@
-/** @page conversation-signals Notification Signals
+/** @page notify-signals Notification Signals
 
  @signals
   @signal displaying-userinfo
--- a/finch/finch.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/finch.c	Sat Jul 07 03:52:12 2007 +0000
@@ -381,10 +381,13 @@
 {
 	signal(SIGPIPE, SIG_IGN);
 
+	g_set_prgname("Finch");
+	g_set_application_name(_("Finch"));
+
 	/* Initialize the libpurple stuff */
 	if (!init_libpurple(argc, argv))
 		return 0;
-
+ 
 	purple_blist_show();
 	gnt_main();
 
--- a/finch/gntaccount.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntaccount.c	Sat Jul 07 03:52:12 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,
@@ -645,8 +652,10 @@
 	GList *iter;
 	GntWidget *box, *button;
 
-	if (accounts.window)
+	if (accounts.window) {
+		gnt_window_present(accounts.window);
 		return;
+	}
 
 	accounts.window = gnt_vbox_new(FALSE);
 	gnt_box_set_toplevel(GNT_BOX(accounts.window), TRUE);
--- a/finch/gntblist.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntblist.c	Sat Jul 07 03:52:12 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;
@@ -1260,20 +1267,22 @@
 }
 
 static void
-tooltip_for_buddy(PurpleBuddy *buddy, GString *str)
+tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full)
 {
 	PurplePlugin *prpl;
 	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 (g_utf8_collate(purple_buddy_get_name(buddy), alias))
+	if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias))
 		purple_notify_user_info_add_pair(user_info, _("Nickname"), alias);
 
 	tmp = g_strdup_printf("%s (%s)",
@@ -1285,7 +1294,7 @@
 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 	if (prpl_info && prpl_info->tooltip_text) {
-		prpl_info->tooltip_text(buddy, user_info, TRUE);
+		prpl_info->tooltip_text(buddy, user_info, full);
 	}
 
 	if (purple_prefs_get_bool("/finch/blist/idletime")) {
@@ -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);
 }
@@ -1357,7 +1370,7 @@
 		const char *name = purple_buddy_get_name(pr);
 
 		title = g_strdup(name);
-		tooltip_for_buddy(pr, str);
+		tooltip_for_buddy(pr, str, TRUE);
 		for (node = node->child; node; node = node->next) {
 			PurpleBuddy *buddy = (PurpleBuddy*)node;
 			if (offline) {
@@ -1372,11 +1385,11 @@
 			if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy))
 				continue;
 			str = g_string_append(str, "\n----------\n");
-			tooltip_for_buddy(buddy, str);
+			tooltip_for_buddy(buddy, str, FALSE);
 		}
 	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		PurpleBuddy *buddy = (PurpleBuddy *)node;
-		tooltip_for_buddy(buddy, str);
+		tooltip_for_buddy(buddy, str, TRUE);
 		title = g_strdup(purple_buddy_get_name(buddy));
 		if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node))
 			lastseen = purple_blist_node_get_int(node, "last_seen");
@@ -1422,9 +1435,10 @@
 
 	str = make_sure_text_fits(str);
 	gnt_util_get_text_bound(str->str, &w, &h);
-	h = MAX(2, h);
+	h = MAX(1, h);
 	tv = gnt_text_view_new();
 	gnt_widget_set_size(tv, w + 1, h);
+	gnt_text_view_set_flag(GNT_TEXT_VIEW(tv), GNT_TEXT_VIEW_NO_SCROLL);
 	gnt_box_add_widget(GNT_BOX(box), tv);
 
 	gnt_widget_set_position(box, x, y);
@@ -1713,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);
@@ -2123,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)
@@ -2134,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)
 {
@@ -2207,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);
@@ -2249,8 +2271,10 @@
 {
 	if (ggblist == NULL)
 		new_list(list);
-	else if (ggblist->window)
+	else if (ggblist->window) {
+		gnt_window_present(ggblist->window);
 		return;
+	}
 
 	ggblist->window = gnt_vwindow_new(FALSE);
 	gnt_widget_set_name(ggblist->window, "buddylist");
--- a/finch/gntconv.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntconv.c	Sat Jul 07 03:52:12 2007 +0000
@@ -140,13 +140,6 @@
 					break;
 			}
 			g_free(error);
-#if 0
-			gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
-					_("Commands are not supported yet. Message was NOT sent."),
-					GNT_TEXT_FLAG_DIM | GNT_TEXT_FLAG_UNDERLINE);
-			gnt_text_view_next_line(GNT_TEXT_VIEW(ggconv->tv));
-			gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 0);
-#endif
 		}
 		else
 		{
@@ -451,6 +444,13 @@
 }
 
 static void
+completion_cb(GntEntry *entry, const char *start, const char *end)
+{
+	if (start == entry->start)
+		gnt_widget_key_pressed(GNT_WIDGET(entry), ": ");
+}
+
+static void
 finch_create_conversation(PurpleConversation *conv)
 {
 	FinchConv *ggc = conv->ui_data;
@@ -541,7 +541,10 @@
 	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);
 
 	gnt_widget_set_position(ggc->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
--- a/finch/gntdebug.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntdebug.c	Sat Jul 07 03:52:12 2007 +0000
@@ -221,56 +221,60 @@
 
 void finch_debug_window_show()
 {
+	GntWidget *wid, *box;
+
 	debug.paused = FALSE;
-	if (debug.window == NULL)
-	{
-		GntWidget *wid, *box;
-		debug.window = gnt_vbox_new(FALSE);
-		gnt_box_set_toplevel(GNT_BOX(debug.window), TRUE);
-		gnt_box_set_title(GNT_BOX(debug.window), _("Debug Window"));
-		gnt_box_set_pad(GNT_BOX(debug.window), 0);
-		gnt_box_set_alignment(GNT_BOX(debug.window), GNT_ALIGN_MID);
+	if (debug.window) {
+		gnt_window_present(debug.window);
+		return;
+	}
 
-		debug.tview = gnt_text_view_new();
-		gnt_box_add_widget(GNT_BOX(debug.window), debug.tview);
-		gnt_widget_set_size(debug.tview,
-				purple_prefs_get_int(PREF_ROOT "/size/width"),
-				purple_prefs_get_int(PREF_ROOT "/size/height"));
-		g_signal_connect(G_OBJECT(debug.tview), "size_changed", G_CALLBACK(size_changed_cb), NULL);
+	debug.window = gnt_vbox_new(FALSE);
+	gnt_box_set_toplevel(GNT_BOX(debug.window), TRUE);
+	gnt_box_set_title(GNT_BOX(debug.window), _("Debug Window"));
+	gnt_box_set_pad(GNT_BOX(debug.window), 0);
+	gnt_box_set_alignment(GNT_BOX(debug.window), GNT_ALIGN_MID);
 
-		gnt_box_add_widget(GNT_BOX(debug.window), gnt_line_new(FALSE));
+	debug.tview = gnt_text_view_new();
+	gnt_box_add_widget(GNT_BOX(debug.window), debug.tview);
+	gnt_widget_set_size(debug.tview,
+			purple_prefs_get_int(PREF_ROOT "/size/width"),
+			purple_prefs_get_int(PREF_ROOT "/size/height"));
+	g_signal_connect(G_OBJECT(debug.tview), "size_changed", G_CALLBACK(size_changed_cb), NULL);
 
-		box = gnt_hbox_new(FALSE);
-		gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
-		gnt_box_set_fill(GNT_BOX(box), FALSE);
+	gnt_box_add_widget(GNT_BOX(debug.window), gnt_line_new(FALSE));
+
+	box = gnt_hbox_new(FALSE);
+	gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
+	gnt_box_set_fill(GNT_BOX(box), FALSE);
 
-		/* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
-		 * it's necessary to make the width of the debug window resizable ... like I said,
-		 * it doesn't make sense. The bug is likely in the packing in gntbox.c.
-		 */
-		wid = gnt_button_new(_("Clear"));
-		g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(clear_debug_win), debug.tview);
-		GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y);
-		gnt_box_add_widget(GNT_BOX(box), wid);
+	/* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
+	 * it's necessary to make the width of the debug window resizable ... like I said,
+	 * it doesn't make sense. The bug is likely in the packing in gntbox.c.
+	 */
+	wid = gnt_button_new(_("Clear"));
+	g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(clear_debug_win), debug.tview);
+	GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y);
+	gnt_box_add_widget(GNT_BOX(box), wid);
 
-		debug.search = gnt_entry_new(purple_prefs_get_string(PREF_ROOT "/filter"));
-		gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Filter: ")));
-		gnt_box_add_widget(GNT_BOX(box), debug.search);
-		g_signal_connect(G_OBJECT(debug.search), "text_changed", G_CALLBACK(update_filter_string), NULL);
+	debug.search = gnt_entry_new(purple_prefs_get_string(PREF_ROOT "/filter"));
+	gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Filter: ")));
+	gnt_box_add_widget(GNT_BOX(box), debug.search);
+	g_signal_connect(G_OBJECT(debug.search), "text_changed", G_CALLBACK(update_filter_string), NULL);
 
-		wid = gnt_check_box_new(_("Pause"));
-		g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_pause), NULL);
-		GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y);
-		gnt_box_add_widget(GNT_BOX(box), wid);
+	wid = gnt_check_box_new(_("Pause"));
+	g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_pause), NULL);
+	GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y);
+	gnt_box_add_widget(GNT_BOX(box), wid);
 
-		gnt_box_add_widget(GNT_BOX(debug.window), box);
-		GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_GROW_Y);
+	gnt_box_add_widget(GNT_BOX(debug.window), box);
+	GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_GROW_Y);
 
-		gnt_widget_set_name(debug.window, "debug-window");
+	gnt_widget_set_name(debug.window, "debug-window");
 
-		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);
-	}
+	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/gntft.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntft.c	Sat Jul 07 03:52:12 2007 +0000
@@ -178,6 +178,7 @@
 	GntWidget *button;
 	GntWidget *checkbox;
 	GntWidget *tree;
+	int widths[] = {8, 12, 8, 8, 8, 8, -1};
 
 	if (!xfer_dialog)
 		xfer_dialog = g_new0(PurpleGntXferDialog, 1);
@@ -195,12 +196,12 @@
 
 	xfer_dialog->tree = tree = gnt_tree_new_with_columns(NUM_COLUMNS);
 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Progress"), _("Filename"), _("Size"), _("Speed"), _("Remaining"), _("Status"));
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_PROGRESS, 8);
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_FILENAME, 8);
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_SIZE, 10);
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_SPEED, 10);
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_REMAINING, 10);
-	gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_STATUS, 10);
+	gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths);
+	gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_PROGRESS, FALSE);
+	gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SIZE, FALSE);
+	gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SPEED, FALSE);
+	gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_REMAINING, FALSE);
+	gnt_widget_set_size(tree, 70, -1);
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
 	gnt_box_add_widget(GNT_BOX(window), tree);
 
@@ -262,6 +263,8 @@
 {
 	if (xfer_dialog == NULL)
 		finch_xfer_dialog_new();
+	else
+		gnt_window_present(xfer_dialog->window);
 }
 
 void
--- a/finch/gntnotify.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntnotify.c	Sat Jul 07 03:52:12 2007 +0000
@@ -304,7 +304,7 @@
 	PurpleAccount *account = g_object_get_data(G_OBJECT(widget), "notify-account");
 	gpointer data = g_object_get_data(G_OBJECT(widget), "notify-data");
 
-	list = gnt_tree_get_selection_text_list(GNT_TREE(widget));
+	list = gnt_tree_get_selection_text_list(GNT_TREE(g_object_get_data(G_OBJECT(widget), "notify-tree")));
 
 	b->callback(purple_account_get_connection(account), list, data);
 	g_list_foreach(list, (GFunc)g_free, NULL);
@@ -335,23 +335,31 @@
 {
 	GntWidget *window, *tree, *box, *button;
 	GList *iter;
+	int columns, i;
 
 	window = gnt_vbox_new(FALSE);
 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
 	gnt_box_set_title(GNT_BOX(window), title);
-	gnt_box_set_fill(GNT_BOX(window), FALSE);
+	gnt_box_set_fill(GNT_BOX(window), TRUE);
 	gnt_box_set_pad(GNT_BOX(window), 0);
 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
 
-	gnt_box_add_widget(GNT_BOX(window),
+	if (primary)
+		gnt_box_add_widget(GNT_BOX(window),
 			gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD));
-	gnt_box_add_widget(GNT_BOX(window),
+	if (secondary)
+		gnt_box_add_widget(GNT_BOX(window),
 			gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL));
 
-	tree = gnt_tree_new_with_columns(g_list_length(results->columns));
+	columns = purple_notify_searchresults_get_columns_count(results);
+	tree = gnt_tree_new_with_columns(columns);
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
 	gnt_box_add_widget(GNT_BOX(window), tree);
 
+	for (i = 0; i < columns; i++)
+		gnt_tree_set_column_title(GNT_TREE(tree), i, 
+				purple_notify_searchresults_column_get_title(results, i));
+
 	box = gnt_hbox_new(TRUE);
 
 	for (iter = results->buttons; iter; iter = iter->next)
@@ -389,7 +397,8 @@
 		button = gnt_button_new(text);
 		g_object_set_data(G_OBJECT(button), "notify-account", purple_connection_get_account(gc));
 		g_object_set_data(G_OBJECT(button), "notify-data", data);
-		g_signal_connect_swapped(G_OBJECT(button), "activate",
+		g_object_set_data(G_OBJECT(button), "notify-tree", tree);
+		g_signal_connect(G_OBJECT(button), "activate",
 				G_CALLBACK(notify_button_activated), b);
 
 		gnt_box_add_widget(GNT_BOX(box), button);
--- a/finch/gntplugin.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntplugin.c	Sat Jul 07 03:52:12 2007 +0000
@@ -242,8 +242,10 @@
 	GList *iter;
 	GList *seen;
 
-	if (plugins.window)
+	if (plugins.window) {
+		gnt_window_present(plugins.window);
 		return;
+	}
 
 	purple_plugins_probe(G_MODULE_SUFFIX);
 
@@ -277,6 +279,14 @@
 	{
 		PurplePlugin *plug = iter->data;
 
+		if (plug->info->type == PURPLE_PLUGIN_LOADER) {
+			GList *cur;
+			for (cur = PURPLE_PLUGIN_LOADER_INFO(plug)->exts; cur != NULL;
+					 cur = cur->next)
+				purple_plugins_probe(cur->data);
+			continue;
+		}
+
 		if (plug->info->type != PURPLE_PLUGIN_STANDARD ||
 			(plug->info->flags & PURPLE_PLUGIN_FLAG_INVISIBLE) ||
 			plug->error)
--- a/finch/gntpounce.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntpounce.c	Sat Jul 07 03:52:12 2007 +0000
@@ -672,6 +672,7 @@
 	GntWidget *win;
 
 	if (pounces_manager != NULL) {
+		gnt_window_present(pounces_manager->window);
 		return;
 	}
 
--- a/finch/gntprefs.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntprefs.c	Sat Jul 07 03:52:12 2007 +0000
@@ -30,9 +30,16 @@
 #include "gntprefs.h"
 #include "gntrequest.h"
 
+#include "gnt.h"
+#include "gntwidget.h"
+
 #include <string.h>
 
-static GList *freestrings;  /* strings to be freed when the pref-window is closed */
+static struct {
+	GList *freestrings;  /* strings to be freed when the pref-window is closed */
+	gboolean showing;
+	GntWidget *window;
+} pref_request;
 
 void finch_prefs_init()
 {
@@ -45,6 +52,10 @@
 	purple_prefs_add_none("/finch/conversations");
 	purple_prefs_add_bool("/finch/conversations/timestamps", TRUE);
 	purple_prefs_add_bool("/finch/conversations/notify_typing", FALSE);
+
+	purple_prefs_add_none("/finch/filelocations");
+	purple_prefs_add_path("/finch/filelocations/last_save_folder", "");
+	purple_prefs_add_path("/finch/filelocations/last_save_folder", "");
 }
 
 void finch_prefs_update_old()
@@ -98,7 +109,7 @@
 		str = g_strdup_printf("%ld", purple_savedstatus_get_creation_time(iter->data));
 		list = g_list_append(list, (char*)purple_savedstatus_get_title(iter->data));
 		list = g_list_append(list, str);
-		freestrings = g_list_prepend(freestrings, str);
+		pref_request.freestrings = g_list_prepend(pref_request.freestrings, str);
 	}
 	return list;
 }
@@ -190,22 +201,22 @@
 	{PURPLE_PREF_NONE, NULL, NULL, NULL},
 };
 
-/* XXX: Translate after the freeze */
 static Prefs idle[] =
 {
-	{PURPLE_PREF_STRING, "/purple/away/idle_reporting", "Report Idle time", get_idle_options},
-	{PURPLE_PREF_BOOLEAN, "/purple/away/away_when_idle", "Change status when idle", NULL},
-	{PURPLE_PREF_INT, "/purple/away/mins_before_away", "Minutes before changing status", NULL},
-	{PURPLE_PREF_INT, "/purple/savedstatus/idleaway", "Change status to", get_status_titles},
+	{PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options},
+	{PURPLE_PREF_BOOLEAN, "/purple/away/away_when_idle", N_("Change status when idle"), NULL},
+	{PURPLE_PREF_INT, "/purple/away/mins_before_away", N_("Minutes before changing status"), NULL},
+	{PURPLE_PREF_INT, "/purple/savedstatus/idleaway", N_("Change status to"), get_status_titles},
 	{PURPLE_PREF_NONE, NULL, NULL, NULL},
 };
 
 static void
 free_strings()
 {
-	g_list_foreach(freestrings, (GFunc)g_free, NULL);
-	g_list_free(freestrings);
-	freestrings = NULL;
+	g_list_foreach(pref_request.freestrings, (GFunc)g_free, NULL);
+	g_list_free(pref_request.freestrings);
+	pref_request.freestrings = NULL;
+	pref_request.showing = FALSE;
 }
 
 static void
@@ -236,6 +247,11 @@
 {
 	PurpleRequestFields *fields;
 
+	if (pref_request.showing) {
+		gnt_window_present(pref_request.window);
+		return;
+	}
+
 	fields = purple_request_fields_new();
 
 	add_pref_group(fields, _("Buddy List"), blist);
@@ -243,7 +259,8 @@
 	add_pref_group(fields, _("Logging"), logging);
 	add_pref_group(fields, _("Idle"), idle);
 
-	purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
+	pref_request.showing = TRUE;
+	pref_request.window = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
 			_("Save"), G_CALLBACK(save_cb), _("Cancel"), free_strings,
 			NULL, NULL, NULL,
 			NULL);
--- a/finch/gntrequest.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntrequest.c	Sat Jul 07 03:52:12 2007 +0000
@@ -42,6 +42,7 @@
 	void *user_data;
 	GntWidget *dialog;
 	GCallback *cbs;
+	gboolean save;
 } PurpleGntFileRequest;
 
 static GntWidget *
@@ -67,8 +68,35 @@
 	return window;
 }
 
+/**
+ * If the window is closed by the wm (ie, without triggering any of
+ * the buttons, then do some default callback.
+ */
+static void
+setup_default_callback(GntWidget *window, gpointer default_cb, gpointer data)
+{
+	g_object_set_data(G_OBJECT(window), "default-callback", default_cb);
+	g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(default_cb), data);
+}
+
+static void
+action_performed(GntWidget *button, gpointer data)
+{
+	g_signal_handlers_disconnect_matched(data, G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL,
+			g_object_get_data(data, "default-callback"),
+			 NULL);
+}
+
+/**
+ * window: this is the window
+ * userdata: the userdata to pass to the primary callbacks
+ * cb: the callback
+ * data: data for the callback
+ * (text, primary-callback) pairs, ended by a NULL
+ */
 static GntWidget *
-setup_button_box(gpointer userdata, gpointer cb, gpointer data, ...)
+setup_button_box(GntWidget *win, gpointer userdata, gpointer cb, gpointer data, ...)
 {
 	GntWidget *box, *button;
 	va_list list;
@@ -86,6 +114,7 @@
 		gnt_box_add_widget(GNT_BOX(box), button);
 		g_object_set_data(G_OBJECT(button), "activate-callback", callback);
 		g_object_set_data(G_OBJECT(button), "activate-userdata", userdata);
+		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(action_performed), win);
 		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(cb), data);
 	}
 
@@ -127,10 +156,11 @@
 		gnt_entry_set_masked(GNT_ENTRY(entry), TRUE);
 	gnt_box_add_widget(GNT_BOX(window), entry);
 
-	box = setup_button_box(user_data, notify_input_cb, entry,
+	box = setup_button_box(window, user_data, notify_input_cb, entry,
 			ok_text, ok_cb, cancel_text, cancel_cb, NULL);
 	gnt_box_add_widget(GNT_BOX(window), box);
 
+	setup_default_callback(window, cancel_cb, user_data);
 	gnt_widget_show(window);
 
 	return window;
@@ -189,10 +219,11 @@
 	}
 	gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(default_value + 1));
 
-	box = setup_button_box(user_data, request_choice_cb, combo,
+	box = setup_button_box(window, user_data, request_choice_cb, combo,
 			ok_text, ok_cb, cancel_text, cancel_cb, NULL);
 	gnt_box_add_widget(GNT_BOX(window), box);
 
+	setup_default_callback(window, cancel_cb, user_data);
 	gnt_widget_show(window);
 	
 	return window;
@@ -538,10 +569,11 @@
 	}
 	gnt_box_add_widget(GNT_BOX(window), box);
 
-	box = setup_button_box(userdata, request_fields_cb, allfields,
+	box = setup_button_box(window, userdata, request_fields_cb, allfields,
 			ok, ok_cb, cancel, cancel_cb, NULL);
 	gnt_box_add_widget(GNT_BOX(window), box);
 
+	setup_default_callback(window, cancel_cb, userdata);
 	gnt_widget_show(window);
 
 	g_object_set_data(G_OBJECT(window), "fields", allfields);
@@ -550,7 +582,7 @@
 }
 
 static void
-file_cancel_cb(GntWidget *wid, gpointer fq)
+file_cancel_cb(gpointer fq, GntWidget *wid)
 {
 	PurpleGntFileRequest *data = fq;
 	if (data->cbs[1] != NULL)
@@ -560,13 +592,17 @@
 }
 
 static void
-file_ok_cb(GntWidget *wid, gpointer fq)
+file_ok_cb(gpointer fq, GntWidget *widget)
 {
 	PurpleGntFileRequest *data = fq;
 	char *file = gnt_file_sel_get_selected_file(GNT_FILE_SEL(data->dialog));
+	char *dir = g_path_get_dirname(file);
 	if (data->cbs[0] != NULL)
 		((PurpleRequestFileCb)data->cbs[0])(data->user_data, file);
 	g_free(file);
+	purple_prefs_set_path(data->save ? "/finch/filelocations/last_save_folder" :
+			"/finch/filelocations/last_open_folder", dir);
+	g_free(dir);
 
 	purple_request_close(PURPLE_REQUEST_FILE, data->dialog);
 }
@@ -588,23 +624,34 @@
 	GntWidget *window = gnt_file_sel_new();
 	GntFileSel *sel = GNT_FILE_SEL(window);
 	PurpleGntFileRequest *data = g_new0(PurpleGntFileRequest, 1);
+	const char *path;
 
 	data->user_data = user_data;
 	data->cbs = g_new0(GCallback, 2);
 	data->cbs[0] = ok_cb;
 	data->cbs[1] = cancel_cb;
 	data->dialog = window;
+	data->save = savedialog;
 	gnt_box_set_title(GNT_BOX(window), title ? title : (savedialog ? _("Save File...") : _("Open File...")));
-	gnt_file_sel_set_current_location(sel, purple_home_dir());  /* XXX: */
+
+	path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder");
+	gnt_file_sel_set_current_location(sel, (path && *path) ? path : purple_home_dir());
+
 	if (savedialog)
 		gnt_file_sel_set_suggested_filename(sel, filename);
+
 	g_signal_connect(G_OBJECT(sel->cancel), "activate",
+			G_CALLBACK(action_performed), window);
+	g_signal_connect(G_OBJECT(sel->select), "activate",
+			G_CALLBACK(action_performed), window);
+	g_signal_connect_swapped(G_OBJECT(sel->cancel), "activate",
 			G_CALLBACK(file_cancel_cb), data);
-	g_signal_connect(G_OBJECT(sel->select), "activate",
+	g_signal_connect_swapped(G_OBJECT(sel->select), "activate",
 			G_CALLBACK(file_ok_cb), data);
-	g_signal_connect_swapped(G_OBJECT(window), "destroy",
-			G_CALLBACK(file_request_destroy), data);
 
+	setup_default_callback(window, file_cancel_cb, data);
+	g_object_set_data_full(G_OBJECT(window), "filerequestdata", data,
+			(GDestroyNotify)file_request_destroy);
 	gnt_widget_show(window);
 
 	return window;
--- a/finch/gntstatus.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/gntstatus.c	Sat Jul 07 03:52:12 2007 +0000
@@ -163,8 +163,11 @@
 void finch_savedstatus_show_all()
 {
 	GntWidget *window, *tree, *box, *button;
-	if (statuses.window)
+	int widths[] = {25, 12, 35};
+	if (statuses.window) {
+		gnt_window_present(statuses.window);
 		return;
+	}
 
 	statuses.window = window = gnt_vbox_new(FALSE);
 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
@@ -177,9 +180,8 @@
 	statuses.tree = tree = gnt_tree_new_with_columns(3);
 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Title"), _("Type"), _("Message"));
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
-	gnt_tree_set_col_width(GNT_TREE(tree), 0, 25);
-	gnt_tree_set_col_width(GNT_TREE(tree), 1, 12);
-	gnt_tree_set_col_width(GNT_TREE(tree), 2, 35);
+	gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths);
+	gnt_widget_set_size(tree, 72, 0);
 	gnt_box_add_widget(GNT_BOX(window), tree);
 
 	populate_statuses(GNT_TREE(tree));
--- a/finch/libgnt/Makefile.am	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/Makefile.am	Sat Jul 07 03:52:12 2007 +0000
@@ -26,6 +26,7 @@
 	gntmenu.c \
 	gntmenuitem.c \
 	gntmenuitemcheck.c \
+	gntslider.c \
 	gntstyle.c \
 	gnttextview.c \
 	gnttree.c \
@@ -53,6 +54,7 @@
 	gntmenu.h \
 	gntmenuitem.h \
 	gntmenuitemcheck.h \
+	gntslider.h \
 	gntstyle.h \
 	gnttextview.h \
 	gnttree.h \
--- a/finch/libgnt/gnt-skel.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnt-skel.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gnt-skel.h -skel API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gnt.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnt.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,14 @@
 /**
+ * @defgroup gnt GNT (GLib Ncurses Toolkit)
+ *
+ * GNT is an ncurses toolkit for creating text-mode graphical user interfaces
+ * in a fast and easy way.
+ */
+/**
+ * @file gnt.h GNT API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -102,7 +112,7 @@
 /**
  * 
  * @param label
- * @param callback)()
+ * @param callback
  */
 void gnt_register_action(const char *label, void (*callback)());
 
@@ -139,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/gntbindable.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntbindable.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntbindable.h Bindable API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntbox.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntbox.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntbox.h Box API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -84,9 +88,8 @@
 G_BEGIN_DECLS
 
 /**
- * 
- *
- * @return
+ * The GType for GntBox.
+ * @return The GType.
  */
 GType gnt_box_get_gtype(void);
 
@@ -94,98 +97,110 @@
 #define gnt_hbox_new(homo) gnt_box_new(homo, FALSE)
 
 /**
- * 
- * @param homo
- * @param vert
+ * Create a new GntBox.
  *
- * @return
+ * @param homo  If @c TRUE, all the widgets in it will have the same width (or height)
+ * @param vert  Whether the widgets in it should be stacked vertically (if @c TRUE)
+ *              or horizontally (if @c FALSE).
+ *
+ * @return The new GntBox.
  */
 GntWidget * gnt_box_new(gboolean homo, gboolean vert);
 
 /**
- * 
- * @param box
- * @param widget
+ * Add a widget in the box.
+ *
+ * @param box     The box
+ * @param widget  The widget to add
  */
 void gnt_box_add_widget(GntBox *box, GntWidget *widget);
 
 /**
- * 
- * @param box
- * @param title
+ * Set a title for the box.
+ *
+ * @param box    The box
+ * @param title	 The title to set
  */
 void gnt_box_set_title(GntBox *box, const char *title);
 
 /**
- * 
- * @param box
- * @param pad
+ * Set the padding to use between the widgets in the box.
+ *
+ * @param box The box
+ * @param pad The padding to use
  */
 void gnt_box_set_pad(GntBox *box, int pad);
 
 /**
- * 
- * @param box
- * @param set
+ * Set whether it's a toplevel box (ie, a window) or not. If a box is toplevel,
+ * then it will show borders, the title (if set) and shadow (if enabled in
+ * @e .gntrc)
+ *
+ * @param box The box
+ * @param set @c TRUE if it's a toplevel box, @c FALSE otherwise.
  */
 void gnt_box_set_toplevel(GntBox *box, gboolean set);
 
 /**
- * 
- * @param box
+ * Reposition and refresh the widgets in the box.
+ *
+ * @param box The box
  */
 void gnt_box_sync_children(GntBox *box);
 
 /**
- * 
- * @param box
- * @param alignment
+ * Set the alignment for the widgets in the box.
+ *
+ * @param box       The box
+ * @param alignment The alignment to use
  */
 void gnt_box_set_alignment(GntBox *box, GntAlignment alignment);
 
 /**
- * 
- * @param box
- * @param widget
+ * Remove a widget from the box. Calling this does NOT destroy the removed widget.
+ *
+ * @param box       The box
+ * @param widget    The widget to remove
  */
 void gnt_box_remove(GntBox *box, GntWidget *widget);
 
- /* XXX: does NOT destroy widget */
-
 /**
- * 
- * @param box
+ * Remove all widgets from the box. This DOES destroy all widgets in the box.
+ *
+ * @param box The box
  */
 void gnt_box_remove_all(GntBox *box);
 
-      /* Removes AND destroys all the widgets in it */
-
 /**
- * 
- * @param box
+ * Readjust the size of each child widget, reposition the child widgets and
+ * recalculate the size of the box.
+ *
+ * @param box  The box
  */
 void gnt_box_readjust(GntBox *box);
 
 /**
- * 
- * @param box
- * @param fill
+ * Set whether the widgets in the box should fill the empty spaces.
+ *
+ * @param box   The box
+ * @param fill  Whether the child widgets should fill the empty space
  */
 void gnt_box_set_fill(GntBox *box, gboolean fill);
 
 /**
- * 
- * @param box
- * @param dir
+ * Move the focus from one widget to the other.
+ *
+ * @param box The box
+ * @param dir The direction. If it's 1, then the focus is moved forwards, if it's
+ *            -1, the focus is moved backwards.
  */
 void gnt_box_move_focus(GntBox *box, int dir);
 
-  /* +1 to move forward, -1 for backward */
-
 /**
- * 
- * @param box
- * @param widget
+ * Give focus to a specific child widget.
+ *
+ * @param box       The box
+ * @param widget    The child widget to give focus
  */
 void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget);
 
--- a/finch/libgnt/gntbutton.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntbutton.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntbutton.h Button API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntcheckbox.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntcheckbox.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntcheckbox.h Checkbox API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntclipboard.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntclipboard.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntclipboard.h Clipboard API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntcolors.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntcolors.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntcolors.h Colors API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntcombobox.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntcombobox.c	Sat Jul 07 03:52:12 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/gntcombobox.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntcombobox.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntcombobox.h Combobox API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntentry.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntentry.c	Sat Jul 07 03:52:12 2007 +0000
@@ -25,6 +25,7 @@
 
 #include "gntbox.h"
 #include "gntentry.h"
+#include "gntmarshal.h"
 #include "gntstyle.h"
 #include "gnttree.h"
 #include "gntutils.h"
@@ -32,6 +33,7 @@
 enum
 {
 	SIG_TEXT_CHANGED,
+	SIG_COMPLETION,
 	SIGS,
 };
 static guint signals[SIGS] = { 0 };
@@ -69,9 +71,12 @@
 complete_suggest(GntEntry *entry, const char *text)
 {
 	gboolean changed = FALSE;
+	int offstart = 0, offend = 0;
+
 	if (entry->word) {
 		char *s = get_beginning_of_word(entry);
 		const char *iter = text;
+		offstart = g_utf8_pointer_to_offset(entry->start, s);
 		while (*iter && toupper(*s) == toupper(*iter)) {
 			if (*s != *iter)
 				changed = TRUE;
@@ -81,10 +86,17 @@
 			gnt_entry_key_pressed(GNT_WIDGET(entry), iter);
 			changed = TRUE;
 		}
+		offend = g_utf8_pointer_to_offset(entry->start, entry->cursor);
 	} else {
+		offstart = 0;
 		gnt_entry_set_text_internal(entry, text);
 		changed = TRUE;
+		offend = g_utf8_strlen(text, -1);
 	}
+
+	if (changed)
+		g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0,
+				entry->start + offstart, entry->start + offend);
 	return changed;
 }
 
@@ -179,7 +191,7 @@
 
 	stop = gnt_util_onscreen_width(entry->scroll, entry->end);
 	if (stop < widget->priv.width)
-		whline(widget->window, ENTRY_CHAR, widget->priv.width - stop);
+		mvwhline(widget->window, 0, stop, ENTRY_CHAR, widget->priv.width - stop);
 
 	if (focus)
 		mvwchgat(widget->window, 0, gnt_util_onscreen_width(entry->scroll, entry->cursor),
@@ -687,6 +699,14 @@
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
 
+	signals[SIG_COMPLETION] =
+		g_signal_new("completion",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 0, NULL, NULL,
+					 gnt_closure_marshal_VOID__POINTER_POINTER,
+					 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+
 	gnt_bindable_class_register_action(bindable, "cursor-home", move_start,
 				GNT_KEY_CTRL_A, NULL);
 	gnt_bindable_register_binding(bindable, "cursor-home", GNT_KEY_HOME, NULL);
--- a/finch/libgnt/gntentry.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntentry.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntentry.h Entry API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntfilesel.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntfilesel.c	Sat Jul 07 03:52:12 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/gntfilesel.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntfilesel.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntfilesel.h File selector API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntkeys.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntkeys.c	Sat Jul 07 03:52:12 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/gntkeys.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntkeys.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntkeys.h Keys API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntlabel.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntlabel.c	Sat Jul 07 03:52:12 2007 +0000
@@ -138,8 +138,7 @@
 
 	if (GNT_WIDGET(label)->window)
 	{
-		gnt_widget_hide(GNT_WIDGET(label));
-		gnt_label_size_request(GNT_WIDGET(label));
+		werase(GNT_WIDGET(label)->window);
 		gnt_widget_draw(GNT_WIDGET(label));
 	}
 }
--- a/finch/libgnt/gntlabel.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntlabel.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntlabel.h Label API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntline.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntline.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntline.h Line API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntmain.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmain.c	Sat Jul 07 03:52:12 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;
 
@@ -488,8 +502,12 @@
  * Stuff for 'window management' *
  *********************************/
 
-void gnt_window_present(GntWidget *window) {
-	gnt_wm_raise_window(wm, window);
+void gnt_window_present(GntWidget *window)
+{
+	if (wm->event_stack)
+		gnt_wm_raise_window(wm, window);
+	else
+		gnt_widget_set_urgent(window);
 }
 
 void gnt_screen_occupy(GntWidget *widget)
@@ -618,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	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Sat Jul 07 03:52:12 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,8 +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_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/gntmenu.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmenu.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntmenu.h Menu API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntmenuitem.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmenuitem.c	Sat Jul 07 03:52:12 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	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmenuitem.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntmenuitem.h Menuitem API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -48,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);
@@ -110,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/gntmenuitemcheck.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntmenuitemcheck.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntmenuitemcheck.h Check Menuitem API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/gntslider.c	Sat Jul 07 03:52:12 2007 +0000
@@ -0,0 +1,277 @@
+/**
+ * GNT - The GLib Ncurses Toolkit
+ *
+ * GNT is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "gntcolors.h"
+#include "gntkeys.h"
+#include "gntslider.h"
+#include "gntstyle.h"
+
+enum
+{
+	SIG_VALUE_CHANGED,
+	SIGS,
+};
+
+static guint signals[SIGS] = { 0 };
+
+static GntWidgetClass *parent_class = NULL;
+
+/* returns TRUE if the value was changed */
+static gboolean
+sanitize_value(GntSlider *slider)
+{
+	if (slider->current < slider->min)
+		slider->current = slider->min;
+	else if (slider->current > slider->max)
+		slider->current = slider->max;
+	else
+		return FALSE;
+	return TRUE;
+}
+
+static void
+redraw_slider(GntSlider *slider)
+{
+	GntWidget *widget = GNT_WIDGET(slider);
+	if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED))
+		gnt_widget_draw(widget);
+}
+
+static void
+slider_value_changed(GntSlider *slider)
+{
+	g_signal_emit(slider, signals[SIG_VALUE_CHANGED], 0, slider->current);
+}
+
+static void
+gnt_slider_draw(GntWidget *widget)
+{
+	GntSlider *slider = GNT_SLIDER(widget);
+	int attr = 0;
+	int position, size = 0;
+
+	if (slider->vertical)
+		size = widget->priv.height;
+	else
+		size = widget->priv.width;
+
+	if (gnt_widget_has_focus(widget))
+		attr |= GNT_COLOR_HIGHLIGHT;
+	else
+		attr |= GNT_COLOR_HIGHLIGHT_D;
+
+	if (slider->max != slider->min)
+		position = ((size - 1) * (slider->current - slider->min)) / (slider->max - slider->min);
+	else
+		position = 0;
+	if (slider->vertical) {
+		mvwvline(widget->window, size-position, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL) | A_BOLD,
+				position);
+		mvwvline(widget->window, 0, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+				size-position);
+	} else {
+		mvwhline(widget->window, 0, 0, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL) | A_BOLD,
+				position);
+		mvwhline(widget->window, 0, position, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL),
+				size - position);
+	}
+
+	mvwaddch(widget->window,
+			slider->vertical ? (size - position - 1) : 0,
+			slider->vertical ? 0 : position,
+			ACS_CKBOARD | COLOR_PAIR(attr));
+}
+
+static void
+gnt_slider_size_request(GntWidget *widget)
+{
+	if (GNT_SLIDER(widget)->vertical) {
+		widget->priv.width = 1;
+		widget->priv.height = 5;
+	} else {
+		widget->priv.width = 5;
+		widget->priv.height = 1;
+	}
+}
+
+static void
+gnt_slider_map(GntWidget *widget)
+{
+	if (widget->priv.width == 0 || widget->priv.height == 0)
+		gnt_widget_size_request(widget);
+	GNTDEBUG;
+}
+
+static gboolean
+step_back(GntBindable *bindable, GList *null)
+{
+	GntSlider *slider = GNT_SLIDER(bindable);
+	if (slider->current <= slider->min)
+		return FALSE;
+	gnt_slider_advance_step(slider, -1);
+	return TRUE;
+}
+
+static gboolean
+step_forward(GntBindable *bindable, GList *list)
+{
+	GntSlider *slider = GNT_SLIDER(bindable);
+	if (slider->current >= slider->max)
+		return FALSE;
+	gnt_slider_advance_step(slider, 1);
+	return TRUE;
+}
+
+static void
+gnt_slider_class_init(GntSliderClass *klass)
+{
+	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
+	parent_class = GNT_WIDGET_CLASS(klass);
+	parent_class->draw = gnt_slider_draw;
+	parent_class->map = gnt_slider_map;
+	parent_class->size_request = gnt_slider_size_request;
+
+	klass->changed = NULL;
+
+	signals[SIG_VALUE_CHANGED] =
+		g_signal_new("changed",
+		             G_TYPE_FROM_CLASS(klass),
+		             G_SIGNAL_RUN_LAST,
+		             G_STRUCT_OFFSET(GntSliderClass, changed),
+		             NULL, NULL,
+		             g_cclosure_marshal_VOID__INT,
+		             G_TYPE_NONE, 1, G_TYPE_INT);
+
+	gnt_bindable_class_register_action(bindable, "step-backward", step_back, GNT_KEY_LEFT, NULL);
+	gnt_bindable_register_binding(bindable, "step-backward", GNT_KEY_DOWN, NULL);
+	gnt_bindable_class_register_action(bindable, "step-forward", step_forward, GNT_KEY_RIGHT, NULL);
+	gnt_bindable_register_binding(bindable, "step-forward", GNT_KEY_UP, NULL);
+
+	/* XXX: how would home/end work? */
+
+	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
+}
+
+static void
+gnt_slider_init(GTypeInstance *instance, gpointer class)
+{
+	GntWidget *widget = GNT_WIDGET(instance);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER | GNT_WIDGET_CAN_TAKE_FOCUS);
+	widget->priv.minw = 1;
+	widget->priv.minh = 1;
+	GNTDEBUG;
+}
+
+/******************************************************************************
+ * GntSlider API
+ *****************************************************************************/
+GType
+gnt_slider_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0)
+	{
+		static const GTypeInfo info = {
+			sizeof(GntSliderClass),
+			NULL,                   /* base_init        */
+			NULL,                   /* base_finalize    */
+			(GClassInitFunc)gnt_slider_class_init,
+			NULL,                   /* class_finalize   */
+			NULL,                   /* class_data       */
+			sizeof(GntSlider),
+			0,                      /* n_preallocs      */
+			gnt_slider_init,        /* instance_init    */
+			NULL                    /* value_table      */
+		};
+
+		type = g_type_register_static(GNT_TYPE_WIDGET,
+		                              "GntSlider",
+		                              &info, 0);
+	}
+
+	return type;
+}
+
+GntWidget *gnt_slider_new(gboolean vertical, int max, int min)
+{
+	GntWidget *widget = g_object_new(GNT_TYPE_SLIDER, NULL);
+	GntSlider *slider = GNT_SLIDER(widget);
+
+	slider->vertical = vertical;
+
+	if (vertical) {
+		GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_Y);
+	} else {
+		GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X);
+	}
+
+	gnt_slider_set_range(slider, max, min);
+	slider->step = 1;
+
+	return widget;
+}
+
+void gnt_slider_set_value(GntSlider *slider, int value)
+{
+	if (slider->current == value)
+		return;
+	slider->current = value;
+	sanitize_value(slider);
+	redraw_slider(slider);
+	slider_value_changed(slider);
+}
+
+int gnt_slider_advance_step(GntSlider *slider, int steps)
+{
+	slider->current += steps * slider->step;
+	sanitize_value(slider);
+	redraw_slider(slider);
+	slider_value_changed(slider);
+	return slider->current;
+}
+
+void gnt_slider_set_step(GntSlider *slider, int step)
+{
+	slider->step = step;
+}
+
+void gnt_slider_set_range(GntSlider *slider, int max, int min)
+{
+	slider->max = MAX(max, min);
+	slider->min = MIN(max, min);
+	sanitize_value(slider);
+}
+
+static void
+update_label(GntSlider *slider, int current_value, GntLabel *label)
+{
+	char value[256];
+	g_snprintf(value, sizeof(value), "%d/%d", current_value, slider->max);
+	gnt_label_set_text(label, value);
+}
+
+void gnt_slider_reflect_label(GntSlider *slider, GntLabel *label)
+{
+	g_signal_connect(G_OBJECT(slider), "changed", G_CALLBACK(update_label), label);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/gntslider.h	Sat Jul 07 03:52:12 2007 +0000
@@ -0,0 +1,139 @@
+/**
+ * @file gntslider.h Slider API
+ * @ingroup gnt
+ */
+/*
+ * GNT - The GLib Ncurses Toolkit
+ *
+ * GNT is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef GNT_SLIDER_H
+#define GNT_SLIDER_H
+
+#include "gntwidget.h"
+#include "gnt.h"
+#include "gntlabel.h"
+
+#define GNT_TYPE_SLIDER             (gnt_slider_get_gtype())
+#define GNT_SLIDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_SLIDER, GntSlider))
+#define GNT_SLIDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_SLIDER, GntSliderClass))
+#define GNT_IS_SLIDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_SLIDER))
+#define GNT_IS_SLIDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_SLIDER))
+#define GNT_SLIDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_SLIDER, GntSliderClass))
+
+#define GNT_SLIDER_FLAGS(obj)                (GNT_SLIDER(obj)->priv.flags)
+#define GNT_SLIDER_SET_FLAGS(obj, flags)     (GNT_SLIDER_FLAGS(obj) |= flags)
+#define GNT_SLIDER_UNSET_FLAGS(obj, flags)   (GNT_SLIDER_FLAGS(obj) &= ~(flags))
+
+typedef struct _GntSlider			GntSlider;
+typedef struct _GntSliderPriv		GntSliderPriv;
+typedef struct _GntSliderClass		GntSliderClass;
+
+struct _GntSlider
+{
+	GntWidget parent;
+
+	gboolean vertical;
+
+	int max;        /* maximum value */
+	int min;        /* minimum value */
+	int step;       /* amount to change at each step */
+	int current;    /* current value */
+};
+
+struct _GntSliderClass
+{
+	GntWidgetClass parent;
+
+	void (*changed)(GntSlider *slider, int);
+	void (*gnt_reserved1)(void);
+	void (*gnt_reserved2)(void);
+	void (*gnt_reserved3)(void);
+	void (*gnt_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * @return The GType for GntSlider
+ */
+GType gnt_slider_get_gtype(void);
+
+#define gnt_hslider_new(max, min) gnt_slider_new(FALSE, max, min)
+#define gnt_vslider_new(max, min) gnt_slider_new(TRUE, max, min)
+
+/**
+ * Create a new slider.
+ *
+ * @param orient A vertical slider is created if @c TRUE, otherwise the slider is horizontal.
+ * @param max    The maximum value for the slider
+ * @param min    The minimum value for the slider
+ *
+ * @return  The newly created slider
+ */
+GntWidget * gnt_slider_new(gboolean orient, int max, int min);
+
+/**
+ * Set the range of the slider.
+ *
+ * @param slider  The slider
+ * @param max     The maximum value
+ * @param min     The minimum value
+ */
+void gnt_slider_set_range(GntSlider *slider, int max, int min);
+
+/**
+ * Sets the amount of change at each step.
+ * 
+ * @param slider  The slider
+ * @param step    The amount for each ste
+ */
+void gnt_slider_set_step(GntSlider *slider, int step);
+
+/**
+ * Advance the slider forward or backward.
+ *
+ * @param slider   The slider
+ * @param steps    The number of amounts to change, positive to change
+ *                 forward, negative to change backward
+ *
+ * @return   The value of the slider after the change
+ */
+int gnt_slider_advance_step(GntSlider *slider, int steps);
+
+/**
+ * Set the current value for the slider.
+ *
+ * @param slider  The slider
+ * @param value   The current value
+ */
+void gnt_slider_set_value(GntSlider *slider, int value);
+
+/**
+ * Update a label with the value of the slider whenever the value changes.
+ *
+ * @param slider   The slider
+ * @param label    The label to update
+ */
+void gnt_slider_reflect_label(GntSlider *slider, GntLabel *label);
+
+G_END_DECLS
+
+#endif /* GNT_SLIDER_H */
--- a/finch/libgnt/gntstyle.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Sat Jul 07 03:52:12 2007 +0000
@@ -36,7 +36,6 @@
 static GKeyFile *gkfile;
 #endif
 
-static GHashTable *unknowns;
 static char * str_styles[GNT_STYLES];
 static int int_styles[GNT_STYLES];
 static int bool_styles[GNT_STYLES];
@@ -46,9 +45,11 @@
 	return str_styles[style];
 }
 
-const char *gnt_style_get_from_name(const char *name)
+char *gnt_style_get_from_name(const char *group, const char *key)
 {
-	return g_hash_table_lookup(unknowns, name);
+#if GLIB_CHECK_VERSION(2,6,0)
+	return g_key_file_get_value(gkfile, group, key, NULL);
+#endif
 }
 
 gboolean gnt_style_get_bool(GntStyle style, gboolean def)
@@ -268,7 +269,8 @@
 {
 	GError *error = NULL;
 	gsize nkeys;
-	char **keys = g_key_file_get_keys(kfile, "general", &nkeys, &error);
+	const char *prgname = g_get_prgname();
+	char **keys = NULL;
 	int i;
 	struct
 	{
@@ -281,6 +283,14 @@
 	              {"remember_position", GNT_STYLE_REMPOS},
 	              {NULL, 0}};
 
+	if (prgname && *prgname)
+		keys = g_key_file_get_keys(kfile, prgname, &nkeys, NULL);
+
+	if (keys == NULL) {
+		prgname = "general";
+		keys = g_key_file_get_keys(kfile, prgname, &nkeys, &error);
+	}
+
 	if (error)
 	{
 		g_printerr("GntStyle: %s\n", error->message);
@@ -291,12 +301,8 @@
 		for (i = 0; styles[i].style; i++)
 		{
 			str_styles[styles[i].en] =
-					g_key_file_get_string(kfile, "general", styles[i].style, NULL);
+					g_key_file_get_string(kfile, prgname, styles[i].style, NULL);
 		}
-
-		for (i = 0; i < nkeys; i++)
-			g_hash_table_replace(unknowns, g_strdup(keys[i]),
-					g_strdup(g_key_file_get_string(kfile, "general", keys[i], NULL)));
 	}
 	g_strfreev(keys);
 }
@@ -307,9 +313,9 @@
 #if GLIB_CHECK_VERSION(2,6,0)
 	GError *error = NULL;
 	gkfile = g_key_file_new();
-	unknowns = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 
-	if (!g_key_file_load_from_file(gkfile, filename, G_KEY_FILE_NONE, &error))
+	if (!g_key_file_load_from_file(gkfile, filename,
+                G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error))
 	{
 		g_printerr("GntStyle: %s\n", error->message);
 		g_error_free(error);
@@ -337,7 +343,6 @@
 	for (i = 0; i < GNT_STYLES; i++)
 		g_free(str_styles[i]);
 
-	g_hash_table_destroy(unknowns);
 #if GLIB_CHECK_VERSION(2,6,0)
 	g_key_file_free(gkfile);
 #endif
--- a/finch/libgnt/gntstyle.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntstyle.h Style API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -41,7 +45,7 @@
 
 const char *gnt_style_get(GntStyle style);
 
-const char *gnt_style_get_from_name(const char *key);
+char *gnt_style_get_from_name(const char *group, const char *key);
 
 /**
  * 
--- a/finch/libgnt/gnttextview.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Sat Jul 07 03:52:12 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
@@ -65,6 +67,7 @@
 	int i = 0;
 	GList *lines;
 	int rows, scrcol;
+	gboolean has_scroll = !(view->flags & GNT_TEXT_VIEW_NO_SCROLL);
 
 	wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
 	werase(widget->window);
@@ -111,12 +114,12 @@
 			*end = back;
 		}
 		wattroff(widget->window, A_UNDERLINE | A_BLINK | A_REVERSE);
-		whline(widget->window, ' ', widget->priv.width - line->length - 1);
+		whline(widget->window, ' ', widget->priv.width - line->length - has_scroll);
 	}
 
 	scrcol = widget->priv.width - 1;
 	rows = widget->priv.height - 2;
-	if (rows > 0)
+	if (has_scroll && rows > 0)
 	{
 		int total = g_list_length(g_list_first(view->list));
 		int showing, position, up, down;
@@ -143,11 +146,13 @@
 				ACS_CKBOARD | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D), showing);
 	}
 
-	mvwaddch(widget->window, 0, scrcol,
-			(lines ? ACS_UARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
-	mvwaddch(widget->window, widget->priv.height - 1, scrcol,
-			((view->list && view->list->prev) ? ACS_DARROW : ' ') |
-				COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+	if (has_scroll) {
+		mvwaddch(widget->window, 0, scrcol,
+				(lines ? ACS_UARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+		mvwaddch(widget->window, widget->priv.height - 1, scrcol,
+				((view->list && view->list->prev) ? ACS_DARROW : ' ') |
+					COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
+	}
 
 	GNTDEBUG;
 }
@@ -483,6 +488,8 @@
 	GList *list = view->list;
 	GntTextLine *line;
 	int len;
+	gboolean has_scroll = !(view->flags & GNT_TEXT_VIEW_NO_SCROLL);
+	gboolean wrap_word = !(view->flags & GNT_TEXT_VIEW_WRAP_CHAR);
 
 	if (text == NULL || *text == '\0')
 		return;
@@ -519,7 +526,7 @@
 		}
 
 		line = view->list->data;
-		if (line->length == widget->priv.width - 1) {
+		if (line->length == widget->priv.width - has_scroll) {
 			/* The last added line was exactly the same width as the widget */
 			line = g_new0(GntTextLine, 1);
 			line->soft = TRUE;
@@ -528,15 +535,15 @@
 
 		if ((end = strchr(start, '\r')) != NULL ||
 			(end = strchr(start, '\n')) != NULL) {
-			len = gnt_util_onscreen_width(start, end - 1);
-			if (len >= widget->priv.width - line->length - 1) {
+			len = gnt_util_onscreen_width(start, end - has_scroll);
+			if (len >= widget->priv.width - line->length - has_scroll) {
 				end = NULL;
 			}
 		}
 
 		if (end == NULL)
 			end = gnt_util_onscreen_width_to_pointer(start,
-					widget->priv.width - line->length - 1, &len);
+					widget->priv.width - line->length - has_scroll, &len);
 
 		/* Try to append to the previous segment if possible */
 		if (line->segments) {
@@ -554,7 +561,7 @@
 		}
 
 		oldl = line;
-		if (*end && *end != '\n' && *end != '\r') {
+		if (wrap_word && *end && *end != '\n' && *end != '\r') {
 			const char *tmp = end;
 			while (end && *end != '\n' && *end != '\r' && !g_ascii_isspace(*end)) {
 				end = g_utf8_find_prev_char(seg->start + view->string->str, end);
@@ -783,3 +790,48 @@
 	g_signal_connect(G_OBJECT(widget), "key_pressed", G_CALLBACK(scroll_tv), view);
 }
 
+void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag)
+{
+	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	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnttextview.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gnttextview.h Textview API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -43,6 +47,11 @@
 typedef struct _GntTextViewPriv		GntTextViewPriv;
 typedef struct _GntTextViewClass		GntTextViewClass;
 
+typedef enum {
+	GNT_TEXT_VIEW_NO_SCROLL     = 1 << 0,
+	GNT_TEXT_VIEW_WRAP_CHAR     = 1 << 1,
+} GntTextViewFlag;
+
 struct _GntTextView
 {
 	GntWidget parent;
@@ -51,6 +60,7 @@
 	GList *list;        /* List of GntTextLine */
 
 	GList *tags;       /* A list of tags */
+	GntTextViewFlag flags;
 };
 
 typedef enum
@@ -173,6 +183,21 @@
  */
 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
+ * @param flag  The flag to set
+ */
+void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag);
+
 G_END_DECLS
 
 #endif /* GNT_TEXT_VIEW_H */
--- a/finch/libgnt/gnttree.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnttree.c	Sat Jul 07 03:52:12 2007 +0000
@@ -31,6 +31,10 @@
 #define SEARCH_TIMEOUT 4000   /* 4 secs */
 #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
 {
 	SIG_SELECTION_CHANGED,
@@ -67,6 +71,7 @@
 struct _GntTreeCol
 {
 	char *text;
+	gboolean isbinary;
 	int span;       /* How many columns does it span? */
 };
 
@@ -75,6 +80,38 @@
 static GntWidgetClass *parent_class = NULL;
 static guint signals[SIGS] = { 0 };
 
+static void
+readjust_columns(GntTree *tree)
+{
+	int i, col, total;
+	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) + 1;
+		else
+			total += WIDTH(i) + 1;
+	}
+
+	if (total == 0)
+		return;
+
+	for (i = 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)
+			col = WIDTH(i);
+		else
+			col = (WIDTH(i) * width) / total;
+		gnt_tree_set_col_width(GNT_TREE(tree), i, col);
+	}
+}
+
 /* Move the item at position old to position new */
 static GList *
 g_list_reposition_child(GList *list, int old, int new)
@@ -104,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);
@@ -247,27 +286,28 @@
 	GList *iter;
 	int i;
 	gboolean notfirst = FALSE;
-	int lastvisible = tree->ncol;
-
-	while (lastvisible && tree->columns[lastvisible].invisible)
-		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 (tree->columns[i].invisible)
+		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)
 		{
@@ -295,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, ' ');
@@ -307,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, '~');
@@ -317,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);
@@ -335,11 +379,11 @@
 
 	for (i = 0; i < tree->ncol - 1; i++)
 	{
-		if (!tree->columns[i].invisible) {
+		if (!COLUMN_INVISIBLE(tree, i)) {
 			notfirst = TRUE;
 			NEXT_X;
 		}
-		if (!tree->columns[i+1].invisible && notfirst)
+		if (!COLUMN_INVISIBLE(tree, i+1) && notfirst)
 			mvwaddch(widget->window, y, x, type);
 	}
 }
@@ -383,7 +427,7 @@
 
 		for (i = 0; i < tree->ncol; i++)
 		{
-			if (tree->columns[i].invisible) {
+			if (COLUMN_INVISIBLE(tree, i)) {
 				continue;
 			}
 			mvwaddstr(widget->window, pos, x + 1, tree->columns[i].title);
@@ -554,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 (!tree->columns[i].invisible)
-				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;
 	}
 }
@@ -829,16 +877,10 @@
 gnt_tree_size_changed(GntWidget *widget, int w, int h)
 {
 	GntTree *tree = GNT_TREE(widget);
-	int i;
-	int n = 0;
 	if (widget->priv.width <= 0)
 		return;
-	for (i = 0; i < tree->ncol; ++i)
-		n += tree->columns[i].width;
-	if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
-		tree->columns[tree->ncol - 1].width += widget->priv.width - n - 1 * tree->ncol;
-	else
-		tree->columns[tree->ncol - 1].width += widget->priv.width - n - 2 - 1 * tree->ncol;
+
+	readjust_columns(tree);
 }
 
 static gboolean
@@ -977,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);
 }
 
@@ -1364,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);
@@ -1460,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;
@@ -1491,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);
 	}
@@ -1524,6 +1577,14 @@
 	g_return_if_fail(col < tree->ncol);
 
 	tree->columns[col].width = width;
+	if (tree->columns[col].width_ratio == 0)
+		tree->columns[col].width_ratio = width;
+}
+
+void gnt_tree_set_column_title(GntTree *tree, int index, const char *title)
+{
+	g_free(tree->columns[index].title);
+	tree->columns[index].title = g_strdup(title);
 }
 
 void gnt_tree_set_column_titles(GntTree *tree, ...)
@@ -1591,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 (!tree->columns[i].invisible)
-			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);
 
@@ -1607,9 +1673,56 @@
 	tree->hash = g_hash_table_new_full(hash, eq, kd, free_tree_row);
 }
 
+static void
+set_column_flag(GntTree *tree, int col, GntTreeColumnFlag flag, gboolean set)
+{
+	if (set)
+		tree->columns[col].flags |= flag;
+	else
+		tree->columns[col].flags &= ~flag;
+}
+
 void gnt_tree_set_column_visible(GntTree *tree, int col, gboolean vis)
 {
 	g_return_if_fail(col < tree->ncol);
-	tree->columns[col].invisible = !vis;
+	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)
+{
+	g_return_if_fail(col < tree->ncol);
+	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;
+	for (i = 0; i < tree->ncol && cols[i]; i++) {
+		tree->columns[i].width_ratio = cols[i];
+	}
+}
+
--- a/finch/libgnt/gnttree.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gnttree.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gnttree.h Tree API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -36,10 +40,6 @@
 #define GNT_IS_TREE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_TREE))
 #define GNT_TREE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_TREE, GntTreeClass))
 
-#define GNT_TREE_FLAGS(obj)				(GNT_TREE(obj)->priv.flags)
-#define GNT_TREE_SET_FLAGS(obj, flags)		(GNT_TREE_FLAGS(obj) |= flags)
-#define GNT_TREE_UNSET_FLAGS(obj, flags)	(GNT_TREE_FLAGS(obj) &= ~(flags))
-
 typedef struct _GntTree			GntTree;
 typedef struct _GntTreePriv		GntTreePriv;
 typedef struct _GntTreeClass		GntTreeClass;
@@ -47,6 +47,13 @@
 typedef struct _GntTreeRow		GntTreeRow;
 typedef struct _GntTreeCol		GntTreeCol;
 
+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
 {
 	GntWidget parent;
@@ -70,7 +77,8 @@
 	{
 		int width;
 		char *title;
-		gboolean invisible;
+		int width_ratio;
+		GntTreeColumnFlag flags;
 	} *columns;             /* Would a GList be better? */
 	gboolean show_title;
 	gboolean show_separator; /* Whether to show column separators */
@@ -79,6 +87,7 @@
 	int search_timeout;
 
 	GCompareFunc compare;
+	int lastvisible;
 };
 
 struct _GntTreeClass
@@ -97,284 +106,409 @@
 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);
 
 /**
- * 
- * @param tree
+ * Set the title for a column.
+ *
+ * @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);
+
+/**
+ * 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);
 
-/* This can be useful when, for example, we want to store some data
- * which we don't want/need to display. */
 /**
+ * Set whether a column is visible or not.
+ * This can be useful when, for example, we want to store some data
+ * which we don't want/need to display.
  * 
- * @param tree
- * @param col
- * @param vis
+ * @param tree  The tree
+ * @param col   The index of the column
+ * @param vis   If @c FALSE, the column will not be displayed
  */
 void gnt_tree_set_column_visible(GntTree *tree, int col, gboolean vis);
 
+/**
+ * Set whether a column can be resized to keep the same ratio when the
+ * tree is resized.
+ * 
+ * @param tree  The tree
+ * @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.
+ *
+ * @param tree   The tree
+ * @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[]);
+
 G_END_DECLS
 
 /* 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/gntutils.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntutils.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntutils.h Some utility functions
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -28,87 +32,101 @@
 typedef gpointer (*GDupFunc)(gconstpointer data);
 
 /**
- * 
- * @param text
- * @param width
- * @param height
+ * Compute the width and height required to view the text on the screen.
+ *
+ * @param text     The text to be displayed.
+ * @param width    The width required is set here, if not @c NULL.
+ * @param height   The height required is set here, if not @c NULL.
  */
 void gnt_util_get_text_bound(const char *text, int *width, int *height);
 
-/* excluding *end */
 /**
- * 
- * @param start
- * @param end
+ * Get the onscreen width of a string, or a substring.
  *
- * @return
+ * @param start  The beginning of the string.
+ * @param end    The end of the string. The width returned is the width
+ *               upto (but not including) end. If end is NULL, then start
+ *               is considered as a @c NULL-terminated string.
+ *
+ * @return       The on-screen width of the string.
  */
 int gnt_util_onscreen_width(const char *start, const char *end);
 
+/**
+ * Computes and returns the string after a specific number of onscreen characters.
+ *
+ * @param str  The string.
+ * @param len  The length to consider. If non-positive, the entire screenlength is used.
+ * @param w    The actual width of the string upto the returned offset, if not @c NULL.
+ *
+ * @return     The string after len offset.
+ */
 const char *gnt_util_onscreen_width_to_pointer(const char *str, int len, int *w);
 
-/* Inserts newlines in 'string' where necessary so that its onscreen width is
+/**
+ * Inserts newlines in 'string' where necessary so that its onscreen width is 
  * no more than 'maxw'.
- * 'maxw' can be <= 0, in which case the maximum screen width is considered.
  *
- * Returns a newly allocated string.
- */
-/**
- * 
- * @param string
- * @param maxw
+ * @param string  The string.
+ * @param maxw    The width that the string should fit into. If maxw is <= 0,
+ *                then the available maximum width is used.
  *
- * @return
+ * @return  A newly allocated string that needs to be freed by the caller.
  */
 char * gnt_util_onscreen_fit_string(const char *string, int maxw);
 
 /**
- * 
- * @param src
- * @param hash
- * @param equal
- * @param key_d
- * @param value_d
- * @param key_dup
- * @param value_dup
+ * Duplicate the contents of a hastable.
  *
- * @return
+ * @param src         The source hashtable.
+ * @param hash        The hash-function to use.
+ * @param equal       The hash-equal function to use.
+ * @param key_d       The key-destroy function to use.
+ * @param value_d     The value-destroy function to use.
+ * @param key_dup     The function to use to duplicate the key.
+ * @param value_dup   The function to use to duplicate the value.
+ *
+ * @return    The new hashtable.
  */
 GHashTable * g_hash_table_duplicate(GHashTable *src, GHashFunc hash, GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d, GDupFunc key_dup, GDupFunc value_dup);
 
 /**
  * To be used with g_signal_new. Look in the key_pressed signal-definition in
  * gntwidget.c for usage.
- */
-/**
- * 
- * @param ihint
- * @param return_accu
- * @param handler_return
- * @param dummy
  *
- * @return
+ * @param ihint           NA
+ * @param return_accu     NA
+ * @param handler_return  NA
+ * @param dummy           NA
+ *
+ * @return  NA
  */
 gboolean gnt_boolean_handled_accumulator(GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy);
 
 /**
- * Returns a GntTree populated with "key" -> "binding" for the widget.
- */
-/**
+ * Get a helpful display about the bindings of a widget.
  * 
- * @param widget
+ * @param widget The widget to get bindings for.
  *
- * @return
+ * @return Returns a GntTree populated with "key" -> "binding" for the widget.
  */
 GntWidget * gnt_widget_bindings_view(GntWidget *widget);
 
 /**
- * Parse widgets from 'string'.
- */
-/**
+ * Parse widgets from an XML description. For example,
+ *
+ * @code
+ * GntWidget *win, *button;
+ * gnt_util_parse_widgets("\
+ *      <vwindow id='0' fill='0' align='2'>     \
+ *          <label>This is a test</label>       \
+ *          <button id='1'>OK</button>          \
+ *      </vwindow>",
+ *   2, &win, &button);
+ * @endcode
  * 
- * @param string
- * @param num
+ * @param string  The XML string.
+ * @param num     The number of widgets to return, followed by 'num' GntWidget **
  */
 void gnt_util_parse_widgets(const char *string, int num, ...);
 
--- a/finch/libgnt/gntwidget.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntwidget.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntwidget.h Widget API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntwindow.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntwindow.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntwindow.h Window API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
--- a/finch/libgnt/gntwm.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntwm.c	Sat Jul 07 03:52:12 2007 +0000
@@ -61,6 +61,7 @@
 	SIG_GIVE_FOCUS,
 	SIG_KEY_PRESS,
 	SIG_MOUSE_CLICK,
+	SIG_TERMINAL_REFRESH,
 	SIGS
 };
 
@@ -203,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) {
@@ -376,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
@@ -405,7 +403,6 @@
 switch_window_n(GntBindable *bind, GList *list)
 {
 	GntWM *wm = GNT_WM(bind);
-	GntWidget *w = NULL;
 	GList *l;
 	int n;
 
@@ -417,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;
 }
 
@@ -1031,11 +1022,13 @@
 	endwin();
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
+	refresh();
+	g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
 	update_screen(wm);
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
 	curs_set(0);   /* endwin resets the cursor to normal */
 
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean
@@ -1238,6 +1231,15 @@
 					 gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER,
 					 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER);
 
+	signals[SIG_TERMINAL_REFRESH] = 
+		g_signal_new("terminal-refresh",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 G_STRUCT_OFFSET(GntWMClass, terminal_refresh),
+					 NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
+
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next,
 				"\033" "n", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev,
@@ -1350,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);
 	}
 
@@ -1458,50 +1459,50 @@
 static gboolean
 match_title(gpointer title, gpointer n, gpointer wid_title)
 {
-	/* maybe check for regex.h? */
+	/* XXX: do any regex magic here. */
 	if (g_strrstr((gchar *)wid_title, (gchar *)title))
 		return TRUE;
 	return FALSE;
 }
 
 #if !GLIB_CHECK_VERSION(2,4,0)
-typedef struct
+struct
 {
-	GntWM *wm;
-	GntWS *ret;
-	gchar *title;
-} title_search;
+	gpointer data;
+	gpointer value;
+} table_find_data;
 
-static void match_title_search(gpointer key, gpointer value, gpointer search)
+static void
+table_find_helper(gpointer key, gpointer value, gpointer data)
 {
-	title_search *s = search;
-	if (s->ret)
-		return;
-	if (match_title(key, NULL, s->title))
-		s->ret = g_hash_table_lookup(s->wm->title_places, key);
+	GHRFunc func = data;
+	if (func(key, value, table_find_data.data))
+		table_find_data.value = value;
+}
+
+static gpointer
+g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data)
+{
+	table_find_data.data = data;
+	table_find_data.value = NULL;
+	g_hash_table_foreach(table, table_find_helper, func);
+	return table_find_data.value;
 }
 #endif
 
 static GntWS *
-new_widget_find_workspace(GntWM *wm, GntWidget *widget, gchar *wid_title)
+new_widget_find_workspace(GntWM *wm, GntWidget *widget)
 {
-	GntWS *ret;
-	const gchar *name;
-#if GLIB_CHECK_VERSION(2,4,0)
-	ret = g_hash_table_find(wm->title_places, match_title, wid_title);
-#else
-	title_search *s = NULL;
-	s = g_new0(title_search, 1);
-	s->wm = wm;
-	s->title = wid_title;
-	g_hash_table_foreach(wm->title_places, match_title_search, s);
-	ret = s->ret;
-#endif
+	GntWS *ret = NULL;
+	const gchar *name, *title;
+	title = GNT_BOX(widget)->title;
+	if (title)
+		ret = g_hash_table_find(wm->title_places, match_title, (gpointer)title);
 	if (ret)
 		return ret;
 	name = gnt_widget_get_name(widget);
 	if (name)
-		ret = g_hash_table_lookup(wm->name_places, name);
+		ret = g_hash_table_find(wm->name_places, match_title, (gpointer)name);
 	return ret ? ret : wm->cws;
 }
 
@@ -1561,30 +1562,16 @@
 	if (!transient) {
 		GntWS *ws = wm->cws;
 		if (node->me != wm->_list.window) {
-			GntWidget *w = NULL;
-
 			if (GNT_IS_BOX(widget)) {
-				char *title = GNT_BOX(widget)->title;
-				ws = new_widget_find_workspace(wm, widget, title);
+				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 */
@@ -1965,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	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntwm.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,4 +1,8 @@
 /**
+ * @file gntwm.h Window-manager API
+ * @ingroup gnt
+ */
+/*
  * GNT - The GLib Ncurses Toolkit
  *
  * GNT is the legal property of its developers, whose names are too numerous
@@ -42,6 +46,7 @@
 	GNT_KP_MODE_NORMAL,
 	GNT_KP_MODE_RESIZE,
 	GNT_KP_MODE_MOVE,
+	GNT_KP_MODE_WAIT_ON_CHILD
 } GntKeyPressMode;
 
 typedef struct
@@ -165,10 +170,15 @@
 	 */
 	/*GList *(*window_list)();*/
 
+	/* This is invoked whenever the terminal window is resized, or the
+	 * screen session is attached to a new terminal. (ie, from the
+	 * SIGWINCH callback)
+	 */
+	void (*terminal_refresh)(GntWM *wm);
+
 	void (*res1)(void);
 	void (*res2)(void);
 	void (*res3)(void);
-	void (*res4)(void);
 };
 
 G_BEGIN_DECLS
--- a/finch/libgnt/gntws.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntws.c	Sat Jul 07 03:52:12 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/gntws.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/gntws.h	Sat Jul 07 03:52:12 2007 +0000
@@ -1,3 +1,29 @@
+/**
+ * @file gntws.h Workspace API
+ * @ingroup gnt
+ */
+/*
+ * GNT - The GLib Ncurses Toolkit
+ *
+ * GNT is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
 #ifndef GNTWS_H
 #define GNTWS_H
 
--- a/finch/libgnt/wms/irssi.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/libgnt/wms/irssi.c	Sat Jul 07 03:52:12 2007 +0000
@@ -126,7 +126,7 @@
 	int x, y, w, h;
 
 	name = gnt_widget_get_name(win);
-	if (!name || strcmp(name, "conversation-window")) {
+	if (!name || !strstr(name, "conversation-window")) {
 		if (!GNT_IS_MENU(win) && !GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) {
 			if ((!name || strcmp(name, "buddylist"))) {
 				gnt_widget_get_size(win, &w, &h);
@@ -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;
 }
 
@@ -191,7 +193,7 @@
 {
 	GntWidget *win = node->me;
 	const char *name = gnt_widget_get_name(win);
-	if (!name || !GNT_IS_BOX(win) || strcmp(name, "conversation-window"))
+	if (!name || !GNT_IS_BOX(win) || !strstr(name, "conversation-window"))
 		return;
 	g_object_set_data(G_OBJECT(win), "irssi-index", GINT_TO_POINTER(g_list_index(wm->cws->list, win)));
 	g_timeout_add(0, (GSourceFunc)update_conv_window_title, node);
@@ -248,6 +250,12 @@
 }
 
 static void
+irssi_terminal_refresh(GntWM *wm)
+{
+	draw_line_separators((Irssi*)wm);
+}
+
+static void
 irssi_class_init(IrssiClass *klass)
 {
 	GntWMClass *pclass = GNT_WM_CLASS(klass);
@@ -258,6 +266,7 @@
 	pclass->window_resized = irssi_window_resized;
 	pclass->close_window = irssi_close_window;
 	pclass->window_update = irssi_update_window;
+	pclass->terminal_refresh = irssi_terminal_refresh;
 
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "move-up", move_direction,
 			"\033" "K", GINT_TO_POINTER('k'), NULL);
@@ -274,17 +283,19 @@
 
 void gntwm_init(GntWM **wm)
 {
-	const char *style = NULL;
+	char *style = NULL;
 	Irssi *irssi;
 
 	irssi = g_object_new(TYPE_IRSSI, NULL);
 	*wm = GNT_WM(irssi);
 
-	style = gnt_style_get_from_name("irssi-split-v");
+	style = gnt_style_get_from_name("irssi", "split-v");
 	irssi->vert = style ? atoi(style) : 1;
+	g_free(style);
 
-	style = gnt_style_get_from_name("irssi-split-h");
+	style = gnt_style_get_from_name("irssi", "split-h");
 	irssi->horiz = style ? atoi(style) : 1;
+	g_free(style);
 
 	irssi->vert = MAX(irssi->vert, 1);
 	irssi->horiz = MAX(irssi->horiz, 1);
--- a/finch/plugins/pietray.py	Thu Jul 05 06:48:38 2007 +0000
+++ b/finch/plugins/pietray.py	Sat Jul 07 03:52:12 2007 +0000
@@ -78,9 +78,27 @@
 def toggle_pref(item, pref):
 	purple.PurplePrefsSetBool(pref, item.get_active())
 
+def quit_finch(item, null):
+	# XXX: Ask first
+	purple.PurpleCoreQuit()
+	gtk.main_quit()
+
+def close_docklet(item, null):
+	gtk.main_quit()
+
 def popup_menu(icon, button, tm, none):
 	menu = gtk.Menu()
 
+	#item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+	#item.connect("activate", quit_finch, None)
+	#menu.append(item)
+
+	item = gtk.ImageMenuItem(gtk.STOCK_CLOSE)
+	item.connect("activate", close_docklet, None)
+	menu.append(item)
+
+	menu.append(gtk.MenuItem())
+
 	item = gtk.CheckMenuItem("Blink for unread IM")
 	item.set_active(purple.PurplePrefsGetBool("/plugins/dbus/docklet/blink/im"))
 	item.connect("activate", toggle_pref, "/plugins/dbus/docklet/blink/im")
@@ -176,8 +194,14 @@
 t = gtk.StatusIcon()
 t.connect("popup-menu", popup_menu, None)
 
-init_prefs()
-detect_unread_conversations()
+try:
+	init_prefs()
+	detect_unread_conversations()
+	gtk.main ()
+except:
+	dialog = gtk.Dialog("pietray: Error", None, gtk.DIALOG_NO_SEPARATOR | gtk.DIALOG_MODAL, ("Close", gtk.RESPONSE_CLOSE))
+	dialog.set_resizable(False)
+	dialog.vbox.pack_start(gtk.Label("There was some error. Perhaps a purple client is not running."), False, False, 0)
+	dialog.show_all()
+	dialog.run()
 
-gtk.main ()
-
--- a/libpurple/Makefile.am	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/Makefile.am	Sat Jul 07 03:52:12 2007 +0000
@@ -157,10 +157,10 @@
 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders))
 dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported))
 
-dbus-types.c: dbus-analyze-types.py $(purple_coreheaders)
+dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders)
 	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
 
-dbus-types.h: dbus-analyze-types.py $(dbus_coreheaders)
+dbus-types.h: dbus-analyze-types.py $(purple_build_coreheaders)
 	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
 
 dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported)
--- a/libpurple/blist.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/blist.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref blist-signals
  */
 #ifndef _PURPLE_BLIST_H_
 #define _PURPLE_BLIST_H_
--- a/libpurple/cipher.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/cipher.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref cipher-signals
  */
 #ifndef PURPLE_CIPHER_H
 #define PURPLE_CIPHER_H
--- a/libpurple/conversation.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/conversation.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1563,6 +1563,7 @@
 				purple_conv_chat_is_user_ignored(chat, user);
 
 		cbuddy = purple_conv_chat_cb_new(user, alias, flag);
+		cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
 		/* This seems dumb. Why should we set users thousands of times? */
 		purple_conv_chat_set_users(chat,
 				g_list_prepend(chat->in_room, cbuddy));
@@ -1631,11 +1632,6 @@
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 	g_return_if_fail(prpl_info != NULL);
 
-	flags = purple_conv_chat_user_get_flags(chat, old_user);
-	cb = purple_conv_chat_cb_new(new_user, NULL, flags);
-	purple_conv_chat_set_users(chat,
-		g_list_prepend(chat->in_room, cb));
-
 	if (!strcmp(chat->nick, purple_normalize(conv->account, old_user))) {
 		const char *alias;
 
@@ -1659,6 +1655,12 @@
 			new_alias = purple_buddy_get_contact_alias(buddy);
 	}
 
+	flags = purple_conv_chat_user_get_flags(chat, old_user);
+	cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
+	cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
+	purple_conv_chat_set_users(chat,
+		g_list_prepend(chat->in_room, cb));
+
 	if (ops != NULL && ops->chat_rename_user != NULL)
 		ops->chat_rename_user(conv, old_user, new_user, new_alias);
 
@@ -1949,6 +1951,7 @@
 
 	return chat->left;
 }
+
 PurpleConvChatBuddy *
 purple_conv_chat_cb_new(const char *name, const char *alias, PurpleConvChatBuddyFlags flags)
 {
--- a/libpurple/core.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/core.h	Sat Jul 07 03:52:12 2007 +0000
@@ -20,6 +20,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref core-signals
  */
 #ifndef _PURPLE_CORE_H_
 #define _PURPLE_CORE_H_
--- a/libpurple/dbus-server.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/dbus-server.h	Sat Jul 07 03:52:12 2007 +0000
@@ -22,6 +22,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * @see @ref dbus-server-signals
  */
 
 #ifndef _PURPLE_DBUS_SERVER_H_
--- a/libpurple/dnssrv.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/dnssrv.c	Sat Jul 07 03:52:12 2007 +0000
@@ -50,11 +50,11 @@
 	u_char buf[1024];
 } queryans;
 #else
-static DNS_STATUS WINAPI (*MyDnsQuery_UTF8) (
+static DNS_STATUS (WINAPI *MyDnsQuery_UTF8) (
 	PCSTR lpstrName, WORD wType, DWORD fOptions,
 	PIP4_ARRAY aipServers, PDNS_RECORD* ppQueryResultsSet,
 	PVOID* pReserved) = NULL;
-static void WINAPI (*MyDnsRecordListFree) (PDNS_RECORD pRecordList,
+static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList,
 	DNS_FREE_TYPE FreeType) = NULL;
 #endif
 
--- a/libpurple/imgstore.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/imgstore.c	Sat Jul 07 03:52:12 2007 +0000
@@ -55,8 +55,8 @@
 {
 	PurpleStoredImage *img;
 
-	g_return_val_if_fail(data != NULL, 0);
-	g_return_val_if_fail(size > 0, 0);
+	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(size > 0, NULL);
 
 	img = g_new(PurpleStoredImage, 1);
 	PURPLE_DBUS_REGISTER_POINTER(img, PurpleStoredImage);
@@ -73,11 +73,13 @@
 purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename)
 {
 	PurpleStoredImage *img = purple_imgstore_add(data, size, filename);
-	img->id = ++nextid;
+	if (img) {
+		img->id = ++nextid;
 
-	g_hash_table_insert(imgstore, &(img->id), img);
+		g_hash_table_insert(imgstore, &(img->id), img);
+	}
 
-	return img->id;
+	return (img ? img->id : 0);
 }
 
 PurpleStoredImage *purple_imgstore_find_by_id(int id) {
--- a/libpurple/imgstore.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/imgstore.h	Sat Jul 07 03:52:12 2007 +0000
@@ -22,6 +22,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * @see @ref imgstore-signals
  */
 #ifndef _PURPLE_IMGSTORE_H_
 #define _PURPLE_IMGSTORE_H_
--- a/libpurple/internal.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/internal.h	Sat Jul 07 03:52:12 2007 +0000
@@ -94,8 +94,9 @@
 #include <langinfo.h>
 #endif
 
+#include <gmodule.h>
+
 #ifdef PURPLE_PLUGINS
-# include <gmodule.h>
 # ifndef _WIN32
 #  include <dlfcn.h>
 # endif
@@ -112,11 +113,16 @@
 # include <unistd.h>
 #endif
 
-#ifndef MAXPATHLEN
-# ifdef PATH_MAX
-#  define MAXPATHLEN PATH_MAX
-# else
-#  define MAXPATHLEN 1024
+/* MAXPATHLEN should only be used with readlink() on glib < 2.4.0.  For
+ * anything else, use g_file_read_link() or other dynamic functions.  This is
+ * important because Hurd has no hard limits on path length. */
+#if !GLIB_CHECK_VERSION(2,4,0)
+# ifndef MAXPATHLEN
+#  ifdef PATH_MAX
+#   define MAXPATHLEN PATH_MAX
+#  else
+#   define MAXPATHLEN 1024
+#  endif
 # endif
 #endif
 
--- a/libpurple/log.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/log.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref log-signals
  */
 #ifndef _PURPLE_LOG_H_
 #define _PURPLE_LOG_H_
--- a/libpurple/network.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/network.c	Sat Jul 07 03:52:12 2007 +0000
@@ -517,7 +517,7 @@
 	WSAQUERYSET qs;
 	time_t last_trigger = time(NULL);
 
-	int WSAAPI (*MyWSANSPIoctl) (
+	int (WSAAPI *MyWSANSPIoctl) (
 		HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
 		DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
 		LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
--- a/libpurple/notify.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/notify.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref notify-signals
  */
 #ifndef _PURPLE_NOTIFY_H_
 #define _PURPLE_NOTIFY_H_
--- a/libpurple/plugin.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugin.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,10 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref plugin-signals
+ * @see @ref plugin-ids
+ * @see @ref plugin-i18n
  */
 #ifndef _PURPLE_PLUGIN_H_
 #define _PURPLE_PLUGIN_H_
--- a/libpurple/plugins/autoaccept.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/autoaccept.c	Sat Jul 07 03:52:12 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/filectl.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/filectl.c	Sat Jul 07 03:52:12 2007 +0000
@@ -56,7 +56,7 @@
 		purple_debug_misc("filectl", "read: %s\n", buffer);
 		command = getarg(buffer, 0, 0);
 
-		if (!strncasecmp(command, "login", 6)) {
+		if (!g_ascii_strncasecmp(command, "login", 6)) {
 			PurpleAccount *account;
 
 			arg1 = getarg(buffer, 1, 0);
@@ -69,7 +69,7 @@
 			free(arg1);
 			free(arg2);
 
-		} else if (!strncasecmp(command, "logout", 7)) {
+		} else if (!g_ascii_strncasecmp(command, "logout", 7)) {
 			PurpleAccount *account;
 
 			arg1 = getarg(buffer, 1, 1);
@@ -88,7 +88,7 @@
 
 /* purple_find_conversation() is gone in 2.0.0. */
 #if 0
-		} else if (!strncasecmp(command, "send", 4)) {
+		} else if (!g_ascii_strncasecmp(command, "send", 4)) {
 			PurpleConversation *conv;
 
 			arg1 = getarg(buffer, 1, 0);
@@ -107,21 +107,21 @@
 			free(arg2);
 #endif
 
-		} else if (!strncasecmp(command, "away", 4)) {
+		} else if (!g_ascii_strncasecmp(command, "away", 4)) {
 			arg1 = getarg(buffer, 1, 1);
 			/* serv_set_away_all(arg1); */
 			free(arg1);
 
-		} else if (!strncasecmp(command, "hide", 4)) {
+		} else if (!g_ascii_strncasecmp(command, "hide", 4)) {
 			purple_blist_set_visible(FALSE);
 
-		} else if (!strncasecmp(command, "unhide", 6)) {
+		} else if (!g_ascii_strncasecmp(command, "unhide", 6)) {
 			purple_blist_set_visible(TRUE);
 
-		} else if (!strncasecmp(command, "back", 4)) {
+		} else if (!g_ascii_strncasecmp(command, "back", 4)) {
 			/* do_im_back(); */
 
-		} else if (!strncasecmp(command, "quit", 4)) {
+		} else if (!g_ascii_strncasecmp(command, "quit", 4)) {
 			purple_core_quit();
 
 		}
--- a/libpurple/plugins/log_reader.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1,13 +1,5 @@
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
 #include <stdio.h>
 
-#ifndef PURPLE_PLUGINS
-# define PURPLE_PLUGINS
-#endif
-
 #include "internal.h"
 
 #include "debug.h"
@@ -106,8 +98,8 @@
 				if (sscanf(date, "%u|%u|%u",
 						&tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) {
 
-					purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse",
-							"Filename timestamp parsing error\n");
+					purple_debug_error("Adium log parse",
+					                   "Filename timestamp parsing error\n");
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
@@ -141,8 +133,8 @@
 					if (sscanf(contents2, "%u.%u.%u",
 							&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) {
 
-						purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse",
-								"Contents timestamp parsing error\n");
+						purple_debug_error("Adium log parse",
+						                   "Contents timestamp parsing error\n");
 						g_free(contents);
 						g_free(filename);
 						continue;
@@ -161,7 +153,7 @@
 					log->logger = adium_logger;
 					log->logger_data = data;
 
-					list = g_list_append(list, log);
+					list = g_list_prepend(list, log);
 				}
 			} else if (purple_str_has_suffix(file, ".adiumLog")) {
 				struct tm tm;
@@ -171,8 +163,8 @@
 				if (sscanf(date, "%u|%u|%u",
 						&tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) {
 
-					purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse",
-							"Filename timestamp parsing error\n");
+					purple_debug_error("Adium log parse",
+					                   "Filename timestamp parsing error\n");
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
@@ -201,8 +193,8 @@
 					if (sscanf(contents2, "%u.%u.%u",
 							&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) {
 
-						purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse",
-								"Contents timestamp parsing error\n");
+						purple_debug_error("Adium log parse",
+						                   "Contents timestamp parsing error\n");
 						g_free(contents);
 						g_free(filename);
 						continue;
@@ -222,7 +214,7 @@
 					log->logger = adium_logger;
 					log->logger_data = data;
 
-					list = g_list_append(list, log);
+					list = g_list_prepend(list, log);
 				}
 			}
 		}
@@ -242,17 +234,19 @@
 	gchar *read = NULL;
 	gsize length;
 
+	/* XXX: TODO: We probably want to set PURPLE_LOG_READ_NO_NEWLINE
+	 * XXX: TODO: for HTML logs. */
+	*flags = 0;
+
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
 	data = log->logger_data;
 
 	g_return_val_if_fail(data->path != NULL, g_strdup(""));
 
-	purple_debug(PURPLE_DEBUG_INFO, "Adium log read",
-				"Reading %s\n", data->path);
+	purple_debug_info("Adium log read", "Reading %s\n", data->path);
 	if (!g_file_get_contents(data->path, &read, &length, &error)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "Adium log read",
-				"Error reading log\n");
+		purple_debug_error("Adium log read", "Error reading log\n");
 		if (error)
 			g_error_free(error);
 		return g_strdup("");
@@ -320,6 +314,7 @@
 	data = log->logger_data;
 
 	g_free(data->path);
+	g_free(data);
 }
 
 
@@ -481,7 +476,7 @@
 	if (!(datetime && *datetime))
 	{
 		purple_debug_error("MSN log timestamp parse",
-		                 "Attribute missing: %s\n", "DateTime");
+		                   "Attribute missing: %s\n", "DateTime");
 		return (time_t)0;
 	}
 
@@ -502,7 +497,7 @@
 	if (!(date && *date))
 	{
 		purple_debug_error("MSN log timestamp parse",
-		                 "Attribute missing: %s\n", "Date");
+		                   "Attribute missing: %s\n", "Date");
 		*tm_out = &tm2;
 		return stamp;
 	}
@@ -511,7 +506,7 @@
 	if (!(time && *time))
 	{
 		purple_debug_error("MSN log timestamp parse",
-		                 "Attribute missing: %s\n", "Time");
+		                   "Attribute missing: %s\n", "Time");
 		*tm_out = &tm2;
 		return stamp;
 	}
@@ -519,7 +514,7 @@
 	if (sscanf(date, "%u/%u/%u", &month, &day, &year) != 3)
 	{
 		purple_debug_error("MSN log timestamp parse",
-		                 "%s parsing error\n", "Date");
+		                   "%s parsing error\n", "Date");
 		*tm_out = &tm2;
 		return stamp;
 	}
@@ -536,7 +531,7 @@
 	if (sscanf(time, "%u:%u:%u %c", &hour, &min, &sec, &am_pm) != 4)
 	{
 		purple_debug_error("MSN log timestamp parse",
-		                 "%s parsing error\n", "Time");
+		                   "%s parsing error\n", "Time");
 		*tm_out = &tm2;
 		return stamp;
 	}
@@ -803,12 +798,10 @@
 		logfile = NULL; /* No sense saving the obvious buddy@domain.com. */
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "MSN log read",
-				"Reading %s\n", path);
+	purple_debug_info("MSN log read", "Reading %s\n", path);
 	if (!g_file_get_contents(path, &contents, &length, &error)) {
 		g_free(path);
-		purple_debug(PURPLE_DEBUG_ERROR, "MSN log read",
-				"Error reading log\n");
+		purple_debug_error("MSN log read", "Error reading log\n");
 		if (error)
 			g_error_free(error);
 		return list;
@@ -837,8 +830,8 @@
 
 		session_id = xmlnode_get_attrib(message, "SessionID");
 		if (!session_id) {
-			purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",
-					"Error parsing message: %s\n", "SessionID missing");
+			purple_debug_error("MSN log parse",
+			                   "Error parsing message: %s\n", "SessionID missing");
 			continue;
 		}
 
@@ -864,7 +857,7 @@
 			log->logger = msn_logger;
 			log->logger_data = data;
 
-			list = g_list_append(list, log);
+			list = g_list_prepend(list, log);
 		}
 		old_session_id = session_id;
 	}
@@ -872,7 +865,7 @@
 	if (data)
 		data->last_log = TRUE;
 
-	return list;
+	return g_list_reverse(list);
 }
 
 static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
@@ -881,6 +874,7 @@
 	GString *text = NULL;
 	xmlnode *message;
 
+	*flags = PURPLE_LOG_READ_NO_NEWLINE;
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
 	data = log->logger_data;
@@ -898,8 +892,8 @@
 
 	if (!data->root || !data->message || !data->session_id) {
 		/* Something isn't allocated correctly. */
-		purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",
-				"Error parsing message: %s\n", "Internal variables inconsistent");
+		purple_debug_error("MSN log parse",
+		                   "Error parsing message: %s\n", "Internal variables inconsistent");
 		data->text = text;
 
 		return text->str;
@@ -926,8 +920,8 @@
 
 		/* If this triggers, something is wrong with the XML. */
 		if (!new_session_id) {
-			purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",
-					"Error parsing message: %s\n", "New SessionID missing");
+			purple_debug_error("MSN log parse",
+			                   "Error parsing message: %s\n", "New SessionID missing");
 			break;
 		}
 
@@ -1133,10 +1127,10 @@
 			text = g_string_append(text, style);
 			text = g_string_append(text, "\">");
 			text = g_string_append(text, tmp);
-			text = g_string_append(text, "</span>\n");
+			text = g_string_append(text, "</span><br>");
 		} else {
 			text = g_string_append(text, tmp);
-			text = g_string_append(text, "\n");
+			text = g_string_append(text, "<br>");
 		}
 		g_free(tmp);
 	}
@@ -1176,6 +1170,8 @@
 
 	if (data->text)
 		g_string_free(data->text, FALSE);
+
+	g_free(data);
 }
 
 
@@ -1238,8 +1234,7 @@
 	path = g_build_filename(
 		logdir, prpl_name, filename, NULL);
 
-	purple_debug(PURPLE_DEBUG_INFO, "Trillian log list",
-				"Reading %s\n", path);
+	purple_debug_info("Trillian log list", "Reading %s\n", path);
 	/* FIXME: There's really no need to read the entire file at once.
 	 * See src/log.c:old_logger_list for a better approach.
 	 */
@@ -1252,8 +1247,7 @@
 
 		path = g_build_filename(
 			logdir, prpl_name, "Query", filename, NULL);
-		purple_debug(PURPLE_DEBUG_INFO, "Trillian log list",
-					"Reading %s\n", path);
+		purple_debug_info("Trillian log list", "Reading %s\n", path);
 		if (!g_file_get_contents(path, &contents, &length, &error)) {
 			if (error)
 				g_error_free(error);
@@ -1283,8 +1277,8 @@
 						/* This log had no data, so we remove it. */
 						GList *last = g_list_last(list);
 
-						purple_debug(PURPLE_DEBUG_INFO, "Trillian log list",
-							"Empty log. Offset %i\n", data->offset);
+						purple_debug_info("Trillian log list",
+						                  "Empty log. Offset %i\n", data->offset);
 
 						trillian_logger_finalize((PurpleLog *)last->data);
 						list = g_list_delete_link(list, last);
@@ -1295,7 +1289,7 @@
 				/* The conditional is to make sure we're not reading off
 				 * the end of the string.  We don't want strlen(), as that'd
 				 * have to count the whole string needlessly.
-				 * 
+				 *
 				 * The odd check here is because a Session Start at the
 				 * beginning of the file can be overwritten with a UTF-8
 				 * byte order mark.  Yes, it's weird.
@@ -1348,9 +1342,8 @@
 							&tm.tm_min, &tm.tm_sec,
 							&tm.tm_year) != 5) {
 
-						purple_debug(PURPLE_DEBUG_ERROR,
-							"Trillian log timestamp parse",
-							"Session Start parsing error\n");
+						purple_debug_error("Trillian log timestamp parse",
+						                   "Session Start parsing error\n");
 					} else {
 						PurpleLog *log;
 
@@ -1405,7 +1398,7 @@
 						log->logger = trillian_logger;
 						log->logger_data = data;
 
-						list = g_list_append(list, log);
+						list = g_list_prepend(list, log);
 					}
 				}
 			}
@@ -1420,7 +1413,7 @@
 
 	g_free(prpl_name);
 
-	return list;
+	return g_list_reverse(list);
 }
 
 static char * trillian_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
@@ -1434,6 +1427,7 @@
 	char *c;
 	const char *line;
 
+	*flags = PURPLE_LOG_READ_NO_NEWLINE;
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
 	data = log->logger_data;
@@ -1442,8 +1436,7 @@
 	g_return_val_if_fail(data->length > 0, g_strdup(""));
 	g_return_val_if_fail(data->their_nickname != NULL, g_strdup(""));
 
-	purple_debug(PURPLE_DEBUG_INFO, "Trillian log read",
-				"Reading %s\n", data->path);
+	purple_debug_info("Trillian log read", "Reading %s\n", data->path);
 
 	read = g_malloc(data->length + 2);
 
@@ -1491,7 +1484,7 @@
 		 * ">
 		 * Then, replace the next " " (or add this if the end-of-line is reached) with:
 		 * </a>
-		 * 
+		 *
 		 * As implemented, this isn't perfect, but it should cover common cases.
 		 */
 		while (line && (link = strstr(line, "(Link: ")))
@@ -1689,10 +1682,14 @@
 		if (footer)
 			g_string_append(formatted, footer);
 
-		g_string_append_c(formatted, '\n');
+		g_string_append(formatted, "<br>");
 	}
 
 	g_free(read);
+
+	/* XXX: TODO: What can we do about removing \r characters?
+	 * XXX: TODO: and will that allow us to avoid this
+	 * XXX: TODO: g_strchomp(), or is that unrelated? */
 	/* XXX: TODO: Avoid this g_strchomp() */
 	return g_strchomp(g_string_free(formatted, FALSE));
 }
@@ -1728,9 +1725,369 @@
 
 	g_free(data->path);
 	g_free(data->their_nickname);
-
+	g_free(data);
 }
 
+/*****************************************************************************
+ * QIP Logger                                                           *
+ *****************************************************************************/
+
+/* The QIP logger doesn't write logs, only reads them.  This is to include
+ * QIP logs in the log viewer transparently.
+ */
+#define QIP_LOG_DELIMITER "--------------------------------------"
+#define QIP_LOG_IN_MESSAGE (QIP_LOG_DELIMITER "<-")
+#define QIP_LOG_OUT_MESSAGE (QIP_LOG_DELIMITER ">-")
+#define QIP_LOG_IN_MESSAGE_ESC (QIP_LOG_DELIMITER "&lt;-")
+#define QIP_LOG_OUT_MESSAGE_ESC (QIP_LOG_DELIMITER "&gt;-")
+#define QIP_LOG_TIMEOUT (60*60)
+
+static PurpleLogLogger *qip_logger;
+
+struct qip_logger_data {
+
+	char *path; /* FIXME: Change this to use PurpleStringref like log.c:old_logger_list  */
+	int offset;
+	int length;
+};
+
+static GList *qip_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	const char *logdir;
+	PurplePlugin *plugin;
+	PurplePluginProtocolInfo *prpl_info;
+	char *username;
+	char *filename;
+	char *path;
+	char *contents;
+	struct qip_logger_data *data = NULL;
+	struct tm prev_tm;
+	struct tm tm;
+	gboolean prev_tm_init = FALSE;
+	gboolean main_cycle = TRUE;
+	char *c;
+	char *start_log;
+	char *new_line;
+	int offset = 0;
+	GError *error;
+
+	g_return_val_if_fail(sn != NULL, list);
+	g_return_val_if_fail(account != NULL, list);
+
+	/* QIP only supports ICQ. */
+	if (strcmp(account->protocol_id, "prpl-icq"))
+		return list;
+
+	logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory");
+
+	/* By clearing the log directory path, this logger can be (effectively) disabled. */
+	if (!*logdir)
+		return list;
+
+	plugin = purple_find_prpl(purple_account_get_protocol_id(account));
+	if (!plugin)
+		return NULL;
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
+	if (!prpl_info->list_icon)
+		return NULL;
+
+	username = g_strdup(purple_normalize(account, account->username));
+	filename = g_strdup_printf("%s.txt", purple_normalize(account, sn));
+	path = g_build_filename(logdir, username, "History", filename, NULL);
+	g_free(username);
+	g_free(filename);
+
+	purple_debug_info("QIP logger", "Reading %s\n", path);
+
+	error = NULL;
+	if (!g_file_get_contents(path, &contents, NULL, &error)) {
+		purple_debug_error("QIP logger",
+		                   "Couldn't read file %s: %s \n", path, error->message);
+		g_error_free(error);
+		g_free(path);
+		return list;
+	}
+
+	c = contents;
+	start_log = contents;
+	while (main_cycle) {
+
+		gboolean add_new_log = FALSE;
+
+		if (*c) {
+			if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE) ||
+				purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) {
+
+				char *tmp;
+				
+				new_line = c;
+
+				/* find EOL */
+				c = strstr(c, "\n");
+				c++;
+
+				/* Find the last '(' character. */
+				if ((tmp = strstr(c, "\n")) != NULL) {
+					while (*tmp && *tmp != '(') --tmp;
+					c = tmp;
+				} else {
+					while (*c)
+						c++;
+					c--;
+					c = g_strrstr(c, "(");
+				}
+
+				if (c != NULL) {
+					const char *timestamp = ++c;
+
+					/*  Parse the time, day, month and year  */
+					if (sscanf(timestamp, "%u:%u:%u %u/%u/%u",
+						&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+						&tm.tm_mday, &tm.tm_mon, &tm.tm_year) != 6) {
+
+						purple_debug_error("QIP logger list",
+							"Parsing timestamp error\n");
+					} else {
+						tm.tm_mon -= 1;
+						tm.tm_year -= 1900;
+
+						/* Let the C library deal with
+						 * daylight savings time. */
+						tm.tm_isdst = -1;
+
+						if (!prev_tm_init) {
+							prev_tm = tm;
+							prev_tm_init = TRUE;
+						} else {
+							add_new_log = difftime(mktime(&tm), mktime(&prev_tm)) > QIP_LOG_TIMEOUT;
+						}
+					}
+				}
+			}
+		} else {
+			add_new_log = TRUE;
+			main_cycle = FALSE;
+			new_line = c;
+		}
+
+		/* adding  log */
+		if (add_new_log && prev_tm_init) {
+			PurpleLog *log;
+
+			/* filling data */
+			data = g_new0(struct qip_logger_data, 1);
+			data->path = g_strdup(path);
+			data->length = new_line - start_log;
+			data->offset = offset;
+			offset += data->length;
+			purple_debug_info("QIP logger list",
+				"Creating log: path = (%s); length = (%d); offset = (%d)\n", 
+				data->path, data->length, data->offset);
+
+			/* XXX: Look into this later... Should we pass in a struct tm? */
+			log = purple_log_new(PURPLE_LOG_IM, sn, account,
+				NULL, mktime(&prev_tm), NULL);
+
+			log->logger = qip_logger;
+			log->logger_data = data;
+
+			list = g_list_prepend(list, log);
+
+			prev_tm = tm;
+			start_log = new_line;
+		}
+
+		if (*c) {
+			/* find EOF */
+			c = strstr(c, "\n");
+			c++;
+		}
+	}
+
+	g_free(contents);
+	g_free(path);
+	return g_list_reverse(list);
+}
+
+static char *qip_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	struct qip_logger_data *data;
+	PurpleBuddy *buddy;
+	GString *formatted;
+	char *c;
+	const char *line;
+	gchar *contents;
+	char *selected;
+	GError *error;
+	char *utf8_string;
+	FILE *file;
+
+	*flags = PURPLE_LOG_READ_NO_NEWLINE;
+	g_return_val_if_fail(log != NULL, g_strdup(""));
+
+	data = log->logger_data;
+
+	g_return_val_if_fail(data->path != NULL, g_strdup(""));
+	g_return_val_if_fail(data->length > 0, g_strdup(""));
+
+	error = NULL;
+	
+	contents = g_malloc(data->length + 2);
+
+	file = g_fopen(data->path, "rb");
+	g_return_val_if_fail(file != NULL, g_strdup(""));
+	
+	fseek(file, data->offset, SEEK_SET);
+	fread(contents, data->length, 1, file);
+	fclose(file);
+
+	contents[data->length] = '\n';
+	contents[data->length + 1] = '\0';
+
+	/* Convert file contents from Cp1251 to UTF-8 codeset */
+	error = NULL;
+	if (!(utf8_string = g_convert(contents, -1, "UTF-8", "Cp1251", NULL, NULL, &error))) {
+		purple_debug_error("QIP logger",
+			"Couldn't convert file %s to UTF-8: %s\n", data->path, error->message);
+		g_error_free(error);
+		g_free(contents);
+		return g_strdup("");
+	}
+
+	g_free(contents);
+	contents = g_markup_escape_text(utf8_string, -1);
+	g_free(utf8_string);
+
+	buddy = purple_find_buddy(log->account, log->name);
+
+	/* Apply formatting... */
+	formatted = g_string_sized_new(data->length + 2);
+	c = contents;
+	line = contents;
+
+	while (*c) {
+		gboolean is_in_message = FALSE;
+
+		if (purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC) ||
+		    purple_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) {
+
+			char *tmp;
+			const char *buddy_name;
+
+			is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
+
+			/* find EOL */
+			c = strstr(c, "\n");
+
+			/* XXX: Do we need buddy_name when we have buddy->alias? */
+			buddy_name = ++c;
+
+			/* Find the last '(' character. */
+			if ((tmp = strstr(c, "\n")) != NULL) {
+				while (*tmp && *tmp != '(') --tmp;
+				c = tmp;
+			} else {
+				while (*c)
+					c++;
+				c--;
+				c = g_strrstr(c, "(");
+			}
+
+			if (c != NULL) {
+				const char *timestamp = c;
+				int hour;
+				int min;
+				int sec;
+
+				timestamp++;
+
+				/*  Parse the time, day, month and year */
+				if (sscanf(timestamp, "%u:%u:%u",
+				           &hour, &min, &sec) != 3) {
+					purple_debug_error("QIP logger read",
+					                   "Parsing timestamp error\n");
+				} else {
+					g_string_append(formatted, "<font size=\"2\">");
+					/* TODO: Figure out if we can do anything more locale-independent. */
+					g_string_append_printf(formatted,
+						"(%u:%02u:%02u) %cM ", hour % 12,
+						min, sec, (hour >= 12) ? 'P': 'A');
+					g_string_append(formatted, "</font> ");
+
+					if (is_in_message) {
+						if (buddy_name != NULL && buddy->alias) {
+							g_string_append_printf(formatted,
+								"<span style=\"color: #A82F2F;\">"
+								"<b>%s</b></span>: ", buddy->alias);
+						}
+					} else {
+						const char *acct_name;
+						acct_name = purple_account_get_alias(log->account);
+						if (!acct_name)
+							acct_name = purple_account_get_username(log->account);
+
+						g_string_append_printf(formatted,
+							"<span style=\"color: #16569E;\">"
+							"<b>%s</b></span>: ", acct_name);
+					}
+
+					/* find EOF */
+					c = strstr(c, "\n");
+					line = ++c;
+				}
+			}
+		} else {
+			if ((c = strstr(c, "\n")))
+				*c = '\0';
+
+			if (line[0] != '\n' && line[0] != '\r') {
+
+				g_string_append(formatted, line);
+				g_string_append(formatted, "<br>");
+			}
+			line = ++c;
+		}
+	}
+	g_free(contents);
+
+	/* XXX: TODO: Avoid this g_strchomp() */
+	return g_strchomp(g_string_free(formatted, FALSE));
+}
+
+static int qip_logger_size (PurpleLog *log)
+{
+	struct qip_logger_data *data;
+	char *text;
+	size_t size;
+
+	g_return_val_if_fail(log != NULL, 0);
+
+	data = log->logger_data;
+
+	if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
+		return data ? data->length : 0;
+	}
+
+	text = qip_logger_read(log, NULL);
+	size = strlen(text);
+	g_free(text);
+
+	return size;
+}
+
+static void qip_logger_finalize(PurpleLog *log)
+{
+	struct qip_logger_data *data;
+
+	g_return_if_fail(log != NULL);
+
+	data = log->logger_data;
+
+	g_free(data->path);
+	g_free(data);
+}
 
 /*****************************************************************************
  * Plugin Code                                                               *
@@ -1761,15 +2118,11 @@
 
 	/* Calculate default Adium log directory. */
 #ifdef _WIN32
-		path = "";
+	purple_prefs_add_string("/plugins/core/log_reader/adium/log_directory", "");
 #else
-		path = g_build_filename(purple_home_dir(), "Library", "Application Support",
-			"Adium 2.0", "Users", "Default", "Logs", NULL);
-#endif
-
+	path = g_build_filename(purple_home_dir(), "Library", "Application Support",
+	                        "Adium 2.0", "Users", "Default", "Logs", NULL);
 	purple_prefs_add_string("/plugins/core/log_reader/adium/log_directory", path);
-
-#ifndef _WIN32
 	g_free(path);
 #endif
 
@@ -1779,15 +2132,11 @@
 
 	/* Calculate default Fire log directory. */
 #ifdef _WIN32
-		path = "";
+	purple_prefs_add_string("/plugins/core/log_reader/fire/log_directory", "");
 #else
-		path = g_build_filename(purple_home_dir(), "Library", "Application Support",
-			"Fire", "Sessions", NULL);
-#endif
-
+	path = g_build_filename(purple_home_dir(), "Library", "Application Support",
+	                        "Fire", "Sessions", NULL);
 	purple_prefs_add_string("/plugins/core/log_reader/fire/log_directory", path);
-
-#ifndef _WIN32
 	g_free(path);
 #endif
 
@@ -1799,21 +2148,15 @@
 #ifdef _WIN32
 	folder = wpurple_get_special_folder(CSIDL_PERSONAL);
 	if (folder) {
-#endif
-	path = g_build_filename(
-#ifdef _WIN32
-		folder,
+		path = g_build_filename(folder, "My Chat Logs", NULL);
+		g_free(folder);
+	} else
+		path = g_strdup("");
 #else
-		PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings",
-		g_get_user_name(), "My Documents",
+	path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT,
+	                        "Documents and Settings", g_get_user_name(),
+	                        "My Documents", "My Chat Logs", NULL);
 #endif
-		"My Chat Logs", NULL);
-#ifdef _WIN32
-	g_free(folder);
-	} else /* !folder */
-		path = g_strdup("");
-#endif
-
 	purple_prefs_add_string("/plugins/core/log_reader/messenger_plus/log_directory", path);
 	g_free(path);
 
@@ -1825,21 +2168,15 @@
 #ifdef _WIN32
 	folder = wpurple_get_special_folder(CSIDL_PERSONAL);
 	if (folder) {
-#endif
-	path = g_build_filename(
-#ifdef _WIN32
-		folder,
+		path = g_build_filename(folder, "My Received Files", NULL);
+		g_free(folder);
+	} else
+		path = g_strdup("");
 #else
-		PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings",
-		g_get_user_name(), "My Documents",
+	path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT,
+	                        "Documents and Settings", g_get_user_name(),
+	                        "My Documents", "My Received Files", NULL);
 #endif
-		"My Received Files", NULL);
-#ifdef _WIN32
-	g_free(folder);
-	} else /* !folder */
-		path = g_strdup("");
-#endif
-
 	purple_prefs_add_string("/plugins/core/log_reader/msn/log_directory", path);
 	g_free(path);
 
@@ -1882,7 +2219,7 @@
 		char *folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES);
 		if (folder) {
 			path = g_build_filename(folder, "Trillian",
-					"users", "default", "talk.ini", NULL);
+			                        "users", "default", "talk.ini", NULL);
 			g_free(folder);
 		}
 	}
@@ -1894,25 +2231,25 @@
 #if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */
 		GKeyFile *key_file;
 
-		purple_debug(PURPLE_DEBUG_INFO, "Trillian talk.ini read",
-				"Reading %s\n", path);
+		purple_debug_info("Trillian talk.ini read", "Reading %s\n", path);
+
+		error = NULL;
 		if (!g_key_file_load_from_file(key_file, path, G_KEY_FILE_NONE, GError &error)) {
-			purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read",
-					"Error reading talk.ini\n");
+			purple_debug_error("Trillian talk.ini read",
+			                   "Error reading talk.ini\n");
 			if (error)
 				g_error_free(error);
 		} else {
 			char *logdir = g_key_file_get_string(key_file, "Logging", "Directory", &error);
 			if (error) {
-				purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read",
-						"Error reading Directory value from Logging section\n");
+				purple_debug_error("Trillian talk.ini read",
+				                   "Error reading Directory value from Logging section\n");
 				g_error_free(error);
 			}
 
 			if (logdir) {
 				g_strchomp(logdir);
-				purple_prefs_add_string(
-					"/plugins/core/log_reader/trillian/log_directory", logdir);
+				purple_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", logdir);
 				found = TRUE;
 			}
 
@@ -1922,11 +2259,11 @@
 		gsize length;
 		gchar *contents = NULL;
 
-		purple_debug(PURPLE_DEBUG_INFO, "Trillian talk.ini read",
+		purple_debug_info("Trillian talk.ini read",
 					"Reading %s\n", path);
 		if (!g_file_get_contents(path, &contents, &length, &error)) {
-			purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read",
-					"Error reading talk.ini\n");
+			purple_debug_error("Trillian talk.ini read",
+			                   "Error reading talk.ini\n");
 			if (error)
 				g_error_free(error);
 		} else {
@@ -1957,32 +2294,43 @@
 	} /* path */
 
 	if (!found) {
-#endif /* defined(_WIN32) */
+		folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES);
+		if (folder) {
+			path = g_build_filename(folder, "Trillian", "users",
+			                        "default", "logs", NULL);
+			g_free(folder);
+		} else
+			path = g_strdup("");
+	}
+#else /* !defined(_WIN32) */
+	/* TODO: At some point, this could attempt to parse talk.ini
+	 * TODO: from the default Trillian install directory on the
+	 * TODO: Windows mount point. */
 
 	/* Calculate default Trillian log directory. */
+	path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT,
+	                        "Program Files", "Trillian", "users",
+	                        "default", "logs", NULL);
+#endif
+
+
+	/* Add QIP log directory preference. */
+	purple_prefs_add_none("/plugins/core/log_reader/qip");
+
+	/* Calculate default QIP log directory. */
 #ifdef _WIN32
 	folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES);
 	if (folder) {
-#endif
-	path = g_build_filename(
-#ifdef _WIN32
-		folder,
+		path = g_build_filename(folder, "QIP", "Users", NULL);
+		g_free(folder);
+	} else
+		path = g_strdup("");
 #else
-		PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Program Files",
+	path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT,
+	                        "Program Files", "QIP", "Users", NULL);
 #endif
-		"Trillian", "users", "default", "logs", NULL);
-#ifdef _WIN32
-	g_free(folder);
-	} else /* !folder */
-		path = g_strdup("");
-#endif
-
-	purple_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", path);
+	purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path);
 	g_free(path);
-
-#ifdef _WIN32
-	} /* !found */
-#endif
 }
 
 static gboolean
@@ -2026,11 +2374,24 @@
 												messenger_plus_logger_read,
 												messenger_plus_logger_size);
 	purple_log_logger_add(messenger_plus_logger);
+
 #endif
 
 	/* The names of IM clients are marked for translation at the request of
 	   translators who wanted to transliterate them.  Many translators
 	   choose to leave them alone.  Choose what's best for your language. */
+	qip_logger = purple_log_logger_new("qip", _("QIP"), 6,
+											NULL,
+											NULL,
+											qip_logger_finalize,
+											qip_logger_list,
+											qip_logger_read,
+											qip_logger_size);
+	purple_log_logger_add(qip_logger);
+
+	/* The names of IM clients are marked for translation at the request of
+	   translators who wanted to transliterate them.  Many translators
+	   choose to leave them alone.  Choose what's best for your language. */
 	msn_logger = purple_log_logger_new("msn", _("MSN Messenger"), 6,
 									 NULL,
 									 NULL,
@@ -2067,6 +2428,7 @@
 #endif
 	purple_log_logger_remove(msn_logger);
 	purple_log_logger_remove(trillian_logger);
+	purple_log_logger_remove(qip_logger);
 
 	return TRUE;
 }
@@ -2116,6 +2478,10 @@
 #endif
 
 	ppref = purple_plugin_pref_new_with_name_and_label(
+		"/plugins/core/log_reader/qip/log_directory", _("QIP"));
+	purple_plugin_pref_frame_add(frame, ppref);
+
+	ppref = purple_plugin_pref_new_with_name_and_label(
 		"/plugins/core/log_reader/msn/log_directory", _("MSN Messenger"));
 	purple_plugin_pref_frame_add(frame, ppref);
 
--- a/libpurple/plugins/perl/common/Makefile.mingw	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/perl/common/Makefile.mingw	Sat Jul 07 03:52:12 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	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/perl/perl-common.h	Sat Jul 07 03:52:12 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/plugins/perl/scripts/signals-test.pl	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/plugins/perl/scripts/signals-test.pl	Sat Jul 07 03:52:12 2007 +0000
@@ -7,7 +7,7 @@
 	name => "Perl: $MODULE_NAME",
 	version => "0.1",
 	summary => "Signals Test plugin for the Perl interpreter.",
-	description => "Demonstrate the user of purple signals from " .
+	description => "Demonstrate the use of purple signals from " .
 		       "a perl plugin.",
 	author => "Sadrul Habib Chowdhury <sadrul\@pidgin.im>",
 	url => "http://developer.pidgin.im/wiki/sadrul/",
--- a/libpurple/protocols/bonjour/buddy.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Sat Jul 07 03:52:12 2007 +0000
@@ -17,6 +17,7 @@
 #include <glib.h>
 #include <stdlib.h>
 
+#include "internal.h"
 #include "buddy.h"
 #include "account.h"
 #include "blist.h"
--- a/libpurple/protocols/bonjour/jabber.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Sat Jul 07 03:52:12 2007 +0000
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "internal.h"
 #include "network.h"
 #include "eventloop.h"
 #include "connection.h"
--- a/libpurple/protocols/bonjour/mdns_common.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_common.c	Sat Jul 07 03:52:12 2007 +0000
@@ -16,6 +16,7 @@
 
 #include <string.h>
 
+#include "internal.h"
 #include "config.h"
 #include "mdns_common.h"
 #include "bonjour.h"
--- a/libpurple/protocols/bonjour/mdns_win32.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Sat Jul 07 03:52:12 2007 +0000
@@ -14,6 +14,7 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include "internal.h"
 #include "mdns_win32.h"
 
 #include "debug.h"
--- a/libpurple/protocols/gg/gg.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/gg/gg.c	Sat Jul 07 03:52:12 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/jabber/.todo	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/.todo	Sat Jul 07 03:52:12 2007 +0000
@@ -1,13 +1,4 @@
 <todo version="0.1.19">
-    <note priority="verylow" time="1036043981" done="1089237837">
-        *sigh* file transfer (do we really need/want this?)
-        <comment>
-            faceprint did this
-        </comment>
-    </note>
-    <note priority="high" time="1036043427">
-        problem seeing buddies with long blist?
-    </note>
     <note priority="medium" time="1036044198">
         Browsing
     </note>
@@ -17,12 +8,6 @@
     <note priority="medium" time="1036044448">
         Add option for user info to be published or not in JUD.
     </note>
-    <note priority="medium" time="1036044571">
-        Show self on buddylist
-        <comment>
-            is this done?
-        </comment>
-    </note>
     <note priority="medium" time="1036044583">
         Delete server account.
     </note>
@@ -37,9 +22,6 @@
         <note priority="medium" time="1037893000">
             formatted. enhancement-request so that the birthday field in the setinfo form would split up into relevant fields allowing for a strict syntax (like year--month--day or so, perhaps even dropdown menus)
         </note>
-        <note priority="low" time="1037890968">
-            have set info pre-fill values from the server when no local vcard exists. this will help people migrating to libpurple-based clients
-        </note>
     </note>
     <note priority="verylow" time="1036044192">
         Jabber Transports (having them show up on the buddy list should be fairly easy; having an appropriate right-click menu for them should also be somewhat easy. Providing a UI for adding transports should be rather difficult.)
--- a/libpurple/protocols/jabber/buddy.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sat Jul 07 03:52:12 2007 +0000
@@ -419,6 +419,10 @@
 
 			avatar_data = purple_imgstore_get_data(img);
 			avatar_len = purple_imgstore_get_size(img);
+			/* have to get rid of the old PHOTO if it exists */
+			if((photo = xmlnode_get_child(vc_node, "PHOTO"))) {
+				xmlnode_free(photo);
+			}
 			photo = xmlnode_new_child(vc_node, "PHOTO");
 			binval = xmlnode_new_child(photo, "BINVAL");
 			enc = purple_base64_encode(avatar_data, avatar_len);
--- a/libpurple/protocols/jabber/disco.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sat Jul 07 03:52:12 2007 +0000
@@ -83,6 +83,7 @@
 			SUPPORT_FEATURE("jabber:iq:last")
 			SUPPORT_FEATURE("jabber:iq:oob")
 			SUPPORT_FEATURE("jabber:iq:time")
+			SUPPORT_FEATURE("xmpp:urn:time")
 			SUPPORT_FEATURE("jabber:iq:version")
 			SUPPORT_FEATURE("jabber:x:conference")
 			SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams")
--- a/libpurple/protocols/jabber/iq.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/iq.c	Sat Jul 07 03:52:12 2007 +0000
@@ -169,7 +169,7 @@
 
 static void jabber_iq_time_parse(JabberStream *js, xmlnode *packet)
 {
-	const char *type, *from, *id;
+	const char *type, *from, *id, *xmlns;
 	JabberIq *iq;
 	xmlnode *query;
 	time_t now_t;
@@ -182,27 +182,40 @@
 	from = xmlnode_get_attrib(packet, "from");
 	id = xmlnode_get_attrib(packet, "id");
 
+	/* we're gonna throw this away in a moment, but we need it
+	 * to get the xmlns, so we can figure out if this is
+	 * jabber:iq:time or urn:xmpp:time */
+	query = xmlnode_get_child(packet, "query");
+	xmlns = xmlnode_get_namespace(query);
+
 	if(type && !strcmp(type, "get")) {
+		xmlnode *utc;
 		const char *date;
 
-		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:time");
+		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, xmlns);
 		jabber_iq_set_id(iq, id);
 		xmlnode_set_attrib(iq->node, "to", from);
 
 		query = xmlnode_get_child(iq->node, "query");
 
 		date = purple_utf8_strftime("%Y%m%dT%T", now);
-		xmlnode_insert_data(xmlnode_new_child(query, "utc"), date, -1);
+		utc = xmlnode_new_child(query, "utc");
+		xmlnode_insert_data(utc, date, -1);
+
+		if(!strcmp("urn:xmpp:time", xmlns)) {
+			xmlnode_insert_data(utc, "Z", 1); /* of COURSE the thing that is the same is different */
 
-		date = purple_utf8_strftime("%Z", now);
-		xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1);
+			date = purple_get_tzoff_str(now, TRUE);
+			xmlnode_insert_data(xmlnode_new_child(query, "tzo"), date, -1);
+		} else { /* jabber:iq:time */
+			date = purple_utf8_strftime("%Z", now);
+			xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1);
 
-		date = purple_utf8_strftime("%d %b %Y %T", now);
-		xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1);
+			date = purple_utf8_strftime("%d %b %Y %T", now);
+			xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1);
+		}
 
 		jabber_iq_send(iq);
-	} else {
-		/* XXX: error */
 	}
 }
 
@@ -347,6 +360,7 @@
 	jabber_iq_register_handler("http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse);
 	jabber_iq_register_handler("jabber:iq:last", jabber_iq_last_parse);
 	jabber_iq_register_handler("jabber:iq:time", jabber_iq_time_parse);
+	jabber_iq_register_handler("urn:xmpp:time", jabber_iq_time_parse);
 	jabber_iq_register_handler("jabber:iq:version", jabber_iq_version_parse);
 	jabber_iq_register_handler("http://jabber.org/protocol/disco#info", jabber_disco_info_parse);
 	jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse);
--- a/libpurple/protocols/jabber/jabber.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1116,9 +1116,11 @@
 
 char *jabber_status_text(PurpleBuddy *b)
 {
-	JabberBuddy *jb = jabber_buddy_find(b->account->gc->proto_data, b->name,
-			FALSE);
 	char *ret = NULL;
+	JabberBuddy *jb = NULL;
+	
+	if (b->account->gc && b->account->gc->proto_data)
+		jb = jabber_buddy_find(b->account->gc->proto_data, b->name, FALSE);
 
 	if(jb && !PURPLE_BUDDY_IS_ONLINE(b) && (jb->subscription & JABBER_SUB_PENDING || !(jb->subscription & JABBER_SUB_TO))) {
 		ret = g_strdup(_("Not Authorized"));
@@ -1367,11 +1369,13 @@
 	field = purple_request_field_string_new("password1", _("Password"),
 			"", FALSE);
 	purple_request_field_string_set_masked(field, TRUE);
+	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
 	field = purple_request_field_string_new("password2", _("Password (again)"),
 			"", FALSE);
 	purple_request_field_string_set_masked(field, TRUE);
+	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
 	purple_request_fields(js->gc, _("Change XMPP Password"),
--- a/libpurple/protocols/jabber/presence.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sat Jul 07 03:52:12 2007 +0000
@@ -515,7 +515,7 @@
 		buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
 				jid->node ? "@" : "", jid->domain);
 		if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) {
-			purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)",
+			purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)\n",
 				buddy_name, purple_account_get_username(js->gc->account), js->gc->account);
 			jabber_id_free(jid);
 			g_free(avatar_hash);
@@ -565,9 +565,7 @@
 		}
 
 		if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
-			if(!jbr || jbr == found_jbr) {
-				purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL);
-			}
+			purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL);
 		} else {
 			purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
 		}
--- a/libpurple/protocols/msn/directconn.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sat Jul 07 03:52:12 2007 +0000
@@ -383,7 +383,7 @@
 	else
 	{
 		struct sockaddr_in client_addr;
-		unsigned int client;
+		socklen_t client;
 		fd = accept (source, (struct sockaddr *)&client_addr, &client);
 	}
 
--- a/libpurple/protocols/msn/msnutils.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sat Jul 07 03:52:12 2007 +0000
@@ -329,7 +329,7 @@
 							char *attr_dir;
 							attributes = g_strndup(c, attr_len);
 							attr_dir = purple_markup_get_css_property(attributes, "direction");
-							if (attr_dir && (!strncasecmp(attr_dir, "RTL", 3)))
+							if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
 								direction = '1';
 							g_free(attr_dir);
 							g_free(attributes);
--- a/libpurple/protocols/oscar/family_auth.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Sat Jul 07 03:52:12 2007 +0000
@@ -211,7 +211,7 @@
 
 #ifdef USE_XOR_FOR_ICQ
 	/* If we're signing on an ICQ account then use the older, XOR login method */
-	if (isdigit(sn[0]))
+	if (aim_sn_is_icq(sn))
 		return goddamnicq2(od, conn, sn, password, ci);
 #endif
 
@@ -224,7 +224,7 @@
 
 	/* Truncate ICQ and AOL passwords, if necessary */
 	password_len = strlen(password);
-	if (isdigit(sn[0]) && (password_len > MAXICQPASSLEN))
+	if (aim_sn_is_icq(sn) && (password_len > MAXICQPASSLEN))
 		password_len = MAXICQPASSLEN;
 	else if (truncate_pass && password_len > 8)
 		password_len = 8;
@@ -477,7 +477,7 @@
 		return -EINVAL;
 
 #ifdef USE_XOR_FOR_ICQ
-	if (isdigit(sn[0]))
+	if (aim_sn_is_icq(sn))
 		return goddamnicq(od, conn, sn);
 #endif
 
--- a/libpurple/protocols/oscar/libicq.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/libicq.c	Sat Jul 07 03:52:12 2007 +0000
@@ -32,7 +32,7 @@
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
-	{"gif,jpeg,bmp,ico", 48, 48, 50, 50, 7168,
+	{"gif,jpeg,bmp,ico", 48, 48, 52, 64, 7168,
 		PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
 	oscar_list_icon_icq,		/* list_icon */
 	oscar_list_emblem,		/* list_emblems */
--- a/libpurple/protocols/oscar/odc.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/odc.c	Sat Jul 07 03:52:12 2007 +0000
@@ -314,7 +314,7 @@
 			tmp += size;
 
 			/* Skip past the closing </data> tag */
-			if (strncasecmp(tmp, "</data>", 7))
+			if (g_ascii_strncasecmp(tmp, "</data>", 7))
 			{
 				g_free(embedded_data);
 				break;
--- a/libpurple/protocols/oscar/oscar.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sat Jul 07 03:52:12 2007 +0000
@@ -425,7 +425,7 @@
 		charsetstr1 = "UCS-2BE";
 		charsetstr2 = "UTF-8";
 	} else if (charset == AIM_CHARSET_CUSTOM) {
-		if ((sourcesn != NULL) && isdigit(sourcesn[0]))
+		if ((sourcesn != NULL) && aim_sn_is_icq(sourcesn))
 			charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
 		else
 			charsetstr1 = "ISO-8859-1";
@@ -1258,7 +1258,7 @@
 
 	if (!aim_snvalid(purple_account_get_username(account))) {
 		gchar *buf;
-		buf = g_strdup_printf(_("Unable to login: Could not sign on as %s because the screen name is invalid.  Screen names must either start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
+		buf = g_strdup_printf(_("Unable to login: Could not sign on as %s because the screen name is invalid.  Screen names must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
 		gc->wants_to_die = TRUE;
 		purple_connection_error(gc, buf);
 		g_free(buf);
@@ -4623,7 +4623,7 @@
 
 	if (!aim_snvalid(buddy->name)) {
 		gchar *buf;
-		buf = g_strdup_printf(_("Could not add the buddy %s because the screen name is invalid.  Screen names must either start with a letter and contain only letters, numbers and spaces, or contain only numbers."), buddy->name);
+		buf = g_strdup_printf(_("Could not add the buddy %s because the screen name is invalid.  Screen names must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), buddy->name);
 		if (!purple_conv_present_error(buddy->name, purple_connection_get_account(gc), buf))
 			purple_notify_error(gc, NULL, _("Unable To Add"), buf);
 		g_free(buf);
@@ -5060,7 +5060,8 @@
 			default: { /* La la la */
 				gchar *buf;
 				purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
-				buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason.  The most common reason for this is that you have the maximum number of allowed buddies in your buddy list."), (retval->name ? retval->name : _("(no name)")));
+				buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason."),
+						(retval->name ? retval->name : _("(no name)")));
 				if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
 					purple_notify_error(gc, NULL, _("Unable To Add"), buf);
 				g_free(buf);
--- a/libpurple/protocols/oscar/peer.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/peer.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1019,7 +1019,7 @@
 						PURPLE_DEFAULT_ACTION_NONE,
 						account, sn, NULL,
 						conn, 2,
-						_("_Connect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
+						_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
 						_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
 	}
 	else if (args->type == OSCAR_CAPABILITY_SENDFILE)
--- a/libpurple/protocols/oscar/util.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/oscar/util.c	Sat Jul 07 03:52:12 2007 +0000
@@ -37,6 +37,7 @@
  *   -- DMP.
  *
  */
+/* TODO: Get rid of this and use glib functions */
 int
 aimutil_tokslen(char *toSearch, int theindex, char dl)
 {
@@ -138,6 +139,7 @@
  * Check if the given screen name is a valid AIM screen name.
  * Example: BobDole
  * Example: Henry_Ford@mac.com
+ * Example: 1KrazyKat@example.com
  *
  * @return TRUE if the screen name is valid, FALSE if not.
  */
@@ -146,9 +148,16 @@
 {
 	int i;
 
+	if (purple_email_is_valid(sn))
+		return TRUE;
+
+	/* Normal AIM screen names can't start with a number */
+	if (isdigit(sn[0]))
+		return FALSE;
+
 	for (i = 0; sn[i] != '\0'; i++) {
 		if (!isalnum(sn[i]) && (sn[i] != ' ') &&
-			(sn[i] != '@') && (sn[i] != '.') &&
+			(sn[i] != '.') &&
 			(sn[i] != '_') && (sn[i] != '-'))
 			return FALSE;
 	}
@@ -169,10 +178,10 @@
 
 	for (i = 0; sn[i] != '\0'; i++) {
 		if (!isdigit(sn[i]))
-			return 0;
+			return FALSE;
 	}
 
-	return 1;
+	return TRUE;
 }
 
 /**
@@ -187,14 +196,14 @@
 	int i;
 
 	if (sn[0] != '+')
-		return 0;
+		return FALSE;
 
 	for (i = 1; sn[i] != '\0'; i++) {
 		if (!isdigit(sn[i]))
-			return 0;
+			return FALSE;
 	}
 
-	return 1;
+	return TRUE;
 }
 
 /**
@@ -206,70 +215,37 @@
 aim_snvalid(const char *sn)
 {
 	if ((sn == NULL) || (*sn == '\0'))
-		return 0;
+		return FALSE;
 
-	if (isalpha(sn[0]))
-		return aim_snvalid_aim(sn);
-	else if (isdigit(sn[0]))
-		return aim_snvalid_icq(sn);
-	else if (sn[0] == '+')
-		return aim_snvalid_sms(sn);
-
-	return 0;
+	return aim_snvalid_icq(sn) || aim_snvalid_sms(sn) || aim_snvalid_aim(sn);
 }
 
 /**
  * Determine if a given screen name is an ICQ screen name
- * (i.e. it begins with a number).
+ * (i.e. it is composed of only numbers).
  *
- * @sn A valid AIM or ICQ screen name.
+ * @param sn A valid AIM or ICQ screen name.
  * @return TRUE if the screen name is an ICQ screen name.  Otherwise
  *         FALSE is returned.
  */
 gboolean
 aim_sn_is_icq(const char *sn)
 {
-	if (isalpha(sn[0]))
-		return FALSE;
-	return TRUE;
+	return aim_snvalid_icq(sn);
 }
 
 /**
  * Determine if a given screen name is an SMS number
  * (i.e. it begins with a +).
  *
- * @sn A valid AIM or ICQ screen name.
+ * @param sn A valid AIM or ICQ screen name.
  * @return TRUE if the screen name is an SMS number.  Otherwise
  *         FALSE is returned.
  */
 gboolean
 aim_sn_is_sms(const char *sn)
 {
-	if (sn[0] != '+')
-		return FALSE;
-	return TRUE;
-}
-
-/**
- * This takes a screen name and returns its length without
- * spaces.  If there are no spaces in the SN, then the
- * return is equal to that of strlen().
- */
-int
-aim_snlen(const char *sn)
-{
-	int i = 0;
-
-	if (!sn)
-		return 0;
-
-	while (*sn != '\0') {
-		if (*sn != ' ')
-			i++;
-		sn++;
-	}
-
-	return i;
+	return (sn[0] == '+');
 }
 
 /**
@@ -279,9 +255,9 @@
  * ignored, with the exception that screen names can not start with
  * a space).
  *
- * Return: 0 if equal
- *     non-0 if different
+ * @return 0 if equal, non-0 if different
  */
+/* TODO: Do something different for email addresses. */
 int
 aim_sncmp(const char *sn1, const char *sn2)
 {
--- a/libpurple/protocols/qq/Makefile.mingw	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Sat Jul 07 03:52:12 2007 +0000
@@ -54,7 +54,6 @@
 	group_im.c \
 	group_info.c \
 	group_join.c \
-	group_misc.c \
 	group_network.c \
 	group_opt.c \
 	group_search.c \
--- a/libpurple/protocols/qq/im.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/qq/im.c	Sat Jul 07 03:52:12 2007 +0000
@@ -573,7 +573,7 @@
 		read_packet_b(data, &cursor, len, &reply);
 		if (reply != QQ_SEND_IM_REPLY_OK) {
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n");
-			purple_notify_error(gc, _("Server ACK"), _("Send IM fail\n"), NULL);
+			purple_notify_error(gc, _("Server ACK"), _("Failed to send IM."), NULL);
 		}
 		else
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM ACK OK\n");
--- a/libpurple/protocols/silc/buddy.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/silc/buddy.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1252,7 +1252,7 @@
 			SilcClientEntry entry;
 			silc_dlist_start(clients);
 			while ((entry = silc_dlist_get(clients))) {
-				if (!strncasecmp(b->name, entry->nickname,
+				if (!g_ascii_strncasecmp(b->name, entry->nickname,
 						 strlen(b->name))) {
 					client_entry = entry;
 					break;
--- a/libpurple/protocols/silc/util.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/silc/util.c	Sat Jul 07 03:52:12 2007 +0000
@@ -623,15 +623,15 @@
 	ct = strrchr(filename, '.');
 	if (!ct)
 		return NULL;
-	else if (!strcasecmp(".png", ct))
+	else if (!g_ascii_strcasecmp(".png", ct))
 		return strdup("image/png");
-	else if (!strcasecmp(".jpg", ct))
+	else if (!g_ascii_strcasecmp(".jpg", ct))
 		return strdup("image/jpeg");
-	else if (!strcasecmp(".jpeg", ct))
+	else if (!g_ascii_strcasecmp(".jpeg", ct))
 		return strdup("image/jpeg");
-	else if (!strcasecmp(".gif", ct))
+	else if (!g_ascii_strcasecmp(".gif", ct))
 		return strdup("image/gif");
-	else if (!strcasecmp(".tiff", ct))
+	else if (!g_ascii_strcasecmp(".tiff", ct))
 		return strdup("image/tiff");
 
 	return NULL;
--- a/libpurple/protocols/silc10/buddy.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/silc10/buddy.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1246,7 +1246,7 @@
 			   buddy nickname. */
 			int i;
 			for (i = 0; i < clients_count; i++) {
-				if (!strncasecmp(b->name, clients[i]->nickname,
+				if (!g_ascii_strncasecmp(b->name, clients[i]->nickname,
 						 strlen(b->name))) {
 					clients[0] = clients[i];
 					break;
--- a/libpurple/protocols/simple/simple.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Sat Jul 07 03:52:12 2007 +0000
@@ -337,7 +337,7 @@
 		return;
 	}
 
-	if(!g_strncasecmp(hdr, "NTLM", 4)) {
+	if(!g_ascii_strncasecmp(hdr, "NTLM", 4)) {
 		purple_debug_info("simple", "found NTLM\n");
 		auth->type = 2;
 		parts = g_strsplit(hdr+5, "\", ", 0);
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Sat Jul 07 03:52:12 2007 +0000
@@ -110,7 +110,7 @@
 	}
 
 	/* Yahoo IM 6 spits out 0.png as the URL if the buddy icon is not set */
-	if (who && got_icon_info && url && !strncasecmp(url, "http://", 7)) {
+	if (who && got_icon_info && url && !g_ascii_strncasecmp(url, "http://", 7)) {
 		/* TODO: make this work p2p, try p2p before the url */
 		PurpleUtilFetchUrlData *url_data;
 		struct yahoo_fetch_picture_data *data;
--- a/libpurple/protocols/yahoo/yahoochat.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Sat Jul 07 03:52:12 2007 +0000
@@ -785,44 +785,6 @@
 	g_free(eroom);
 }
 
-/* borrowed from gtkconv.c */
-static gboolean
-meify(char *message, size_t len)
-{
-	/*
-	 * Read /me-ify: If the message (post-HTML) starts with /me,
-	 * remove the "/me " part of it (including that space) and return TRUE.
-	 */
-	char *c;
-	gboolean inside_html = 0;
-
-	/* Umm.. this would be very bad if this happens. */
-	g_return_val_if_fail(message != NULL, FALSE);
-
-	if (len == -1)
-		len = strlen(message);
-
-	for (c = message; *c != '\0'; c++, len--) {
-		if (inside_html) {
-			if (*c == '>')
-				inside_html = FALSE;
-		}
-		else {
-			if (*c == '<')
-				inside_html = TRUE;
-			else
-				break;
-		}
-	}
-
-	if (*c != '\0' && !g_ascii_strncasecmp(c, "/me ", 4)) {
-		memmove(c, c + 4, len - 3);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
 static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *room, const char *what, PurpleMessageFlags flags)
 {
 	struct yahoo_data *yd = gc->proto_data;
@@ -839,7 +801,7 @@
 
 	msg1 = g_strdup(what);
 
-	if (meify(msg1, -1))
+	if (purple_message_meify(msg1, -1))
 		me = 1;
 
 	msg2 = yahoo_html_to_codes(msg1);
--- a/libpurple/protocols/zephyr/ZVariables.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/protocols/zephyr/ZVariables.c	Sat Jul 07 03:52:12 2007 +0000
@@ -186,7 +186,7 @@
 #define max(a,b) ((a > b) ? (a) : (b))
 #endif
 
-    if (strncasecmp(bfr, var, max(strlen(var),cp - bfr)))
+    if (g_strncasecmp(bfr, var, max(strlen(var), cp - bfr)))
 	return(0);			/* var is not the var in
 					   bfr ==> no match */
 
--- a/libpurple/request.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/request.c	Sat Jul 07 03:52:12 2007 +0000
@@ -150,7 +150,8 @@
 		switch (purple_request_field_get_type(field))
 		{
 			case PURPLE_REQUEST_FIELD_STRING:
-				if (purple_request_field_string_get_value(field) == NULL)
+				if (purple_request_field_string_get_value(field) == NULL ||
+				    *(purple_request_field_string_get_value(field)) == '\0')
 					return FALSE;
 
 				break;
--- a/libpurple/savedstatuses.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/savedstatuses.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref savedstatus-signals
  */
 #ifndef _PURPLE_SAVEDSTATUSES_H_
 #define _PURPLE_SAVEDSTATUSES_H_
--- a/libpurple/server.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/server.c	Sat Jul 07 03:52:12 2007 +0000
@@ -198,7 +198,7 @@
 {
 	PurplePluginProtocolInfo *prpl_info = NULL;
 
-	if (b != NULL && b->account->gc->prpl != NULL)
+	if (b != NULL && b->account->gc && b->account->gc->prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl);
 
 	if (b && prpl_info && prpl_info->alias_buddy) {
--- a/libpurple/sound.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/sound.h	Sat Jul 07 03:52:12 2007 +0000
@@ -21,6 +21,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @see @ref sound-signals
  */
 #ifndef _PURPLE_SOUND_H_
 #define _PURPLE_SOUND_H_
--- a/libpurple/tests/check_libpurple.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/tests/check_libpurple.c	Sat Jul 07 03:52:12 2007 +0000
@@ -20,9 +20,17 @@
 
 static PurpleEventLoopUiOps eventloop_ui_ops = {
 	g_timeout_add,
-	(guint (*)(guint))g_source_remove,
+	g_source_remove,
 	purple_check_input_add,
-	(guint (*)(guint))g_source_remove,
+	g_source_remove,
+	NULL, /* input_get_error */
+#if GLIB_CHECK_VERSION(2,14,0)
+	g_timeout_add_seconds,
+#else
+	NULL,
+#endif
+	NULL,
+	NULL,
 	NULL
 };
 
--- a/libpurple/util.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/util.c	Sat Jul 07 03:52:12 2007 +0000
@@ -531,10 +531,9 @@
 }
 #endif
 
-#ifndef HAVE_STRFTIME_Z_FORMAT
-static const char *get_tmoff(const struct tm *tm)
+const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso)
 {
-	static char buf[6];
+	static char buf[7];
 	long off;
 	gint8 min;
 	gint8 hrs;
@@ -554,7 +553,7 @@
 # else
 #  ifdef HAVE_TIMEZONE
 	tzset();
-	off = -timezone;
+	off = -1 * timezone;
 #  endif /* HAVE_TIMEZONE */
 # endif /* !HAVE_TM_GMTOFF */
 #endif /* _WIN32 */
@@ -562,12 +561,22 @@
 	min = (off / 60) % 60;
 	hrs = ((off / 60) - min) / 60;
 
-	if (g_snprintf(buf, sizeof(buf), "%+03d%02d", hrs, ABS(min)) > 5)
-		g_return_val_if_reached("");
+	if(iso) {
+		if (0 == off) {
+			strcpy(buf, "Z");
+		} else {
+			/* please leave the colons...they're optional for iso, but jabber
+			 * wants them */
+			if(g_snprintf(buf, sizeof(buf), "%+03d:%02d", hrs, ABS(min)) > 6)
+				g_return_val_if_reached("");
+		}
+	} else {
+		if (g_snprintf(buf, sizeof(buf), "%+03d%02d", hrs, ABS(min)) > 5)
+			g_return_val_if_reached("");
+	}
 
 	return buf;
 }
-#endif
 
 /* Windows doesn't HAVE_STRFTIME_Z_FORMAT, but this seems clearer. -- rlaager */
 #if !defined(HAVE_STRFTIME_Z_FORMAT) || defined(_WIN32)
@@ -600,7 +609,7 @@
 			                            fmt ? fmt : "",
 			                            c - start - 1,
 			                            start,
-			                            get_tmoff(tm));
+			                            purple_get_tzoff_str(tm, FALSE));
 			g_free(fmt);
 			fmt = tmp;
 			start = c + 1;
@@ -1721,7 +1730,7 @@
 			if (cdata_close_tag)
 			{
 				/* Note: Don't even assume any other tag is a tag in CDATA */
-				if (strncasecmp(str2 + i, cdata_close_tag,
+				if (g_ascii_strncasecmp(str2 + i, cdata_close_tag,
 						strlen(cdata_close_tag)) == 0)
 				{
 					i += strlen(cdata_close_tag) - 1;
@@ -1729,12 +1738,12 @@
 				}
 				continue;
 			}
-			else if (strncasecmp(str2 + i, "<td", 3) == 0 && closing_td_p)
+			else if (g_ascii_strncasecmp(str2 + i, "<td", 3) == 0 && closing_td_p)
 			{
 				str2[j++] = '\t';
 				visible = TRUE;
 			}
-			else if (strncasecmp(str2 + i, "</td>", 5) == 0)
+			else if (g_ascii_strncasecmp(str2 + i, "</td>", 5) == 0)
 			{
 				closing_td_p = TRUE;
 				visible = FALSE;
@@ -1762,7 +1771,7 @@
 
 				/* If we've got an <a> tag with an href, save the address
 				 * to print later. */
-				if (strncasecmp(str2 + i, "<a", 2) == 0 &&
+				if (g_ascii_strncasecmp(str2 + i, "<a", 2) == 0 &&
 				    g_ascii_isspace(str2[i+2]))
 				{
 					int st; /* start of href, inclusive [ */
@@ -1771,7 +1780,7 @@
 					/* Find start of href */
 					for (st = i + 3; st < k; st++)
 					{
-						if (strncasecmp(str2+st, "href=", 5) == 0)
+						if (g_ascii_strncasecmp(str2+st, "href=", 5) == 0)
 						{
 							st += 5;
 							if (str2[st] == '"' || str2[st] == '\'')
@@ -1803,7 +1812,7 @@
 
 				/* Replace </a> with an ascii representation of the
 				 * address the link was pointing to. */
-				else if (href != NULL && strncasecmp(str2 + i, "</a>", 4) == 0)
+				else if (href != NULL && g_ascii_strncasecmp(str2 + i, "</a>", 4) == 0)
 				{
 
 					size_t hrlen = strlen(href);
@@ -1825,29 +1834,29 @@
 				}
 
 				/* Check for tags which should be mapped to newline */
-				else if (strncasecmp(str2 + i, "<p>", 3) == 0
-				 || strncasecmp(str2 + i, "<tr", 3) == 0
-				 || strncasecmp(str2 + i, "<br", 3) == 0
-				 || strncasecmp(str2 + i, "<hr", 3) == 0
-				 || strncasecmp(str2 + i, "<li", 3) == 0
-				 || strncasecmp(str2 + i, "<div", 4) == 0
-				 || strncasecmp(str2 + i, "</table>", 8) == 0)
+				else if (g_ascii_strncasecmp(str2 + i, "<p>", 3) == 0
+				 || g_ascii_strncasecmp(str2 + i, "<tr", 3) == 0
+				 || g_ascii_strncasecmp(str2 + i, "<br", 3) == 0
+				 || g_ascii_strncasecmp(str2 + i, "<hr", 3) == 0
+				 || g_ascii_strncasecmp(str2 + i, "<li", 3) == 0
+				 || g_ascii_strncasecmp(str2 + i, "<div", 4) == 0
+				 || g_ascii_strncasecmp(str2 + i, "</table>", 8) == 0)
 				{
 					str2[j++] = '\n';
 				}
 				/* Check for tags which begin CDATA and need to be closed */
 #if 0 /* FIXME.. option is end tag optional, we can't handle this right now */
-				else if (strncasecmp(str2 + i, "<option", 7) == 0)
+				else if (g_ascii_strncasecmp(str2 + i, "<option", 7) == 0)
 				{
 					/* FIXME: We should not do this if the OPTION is SELECT'd */
 					cdata_close_tag = "</option>";
 				}
 #endif
-				else if (strncasecmp(str2 + i, "<script", 7) == 0)
+				else if (g_ascii_strncasecmp(str2 + i, "<script", 7) == 0)
 				{
 					cdata_close_tag = "</script>";
 				}
-				else if (strncasecmp(str2 + i, "<style", 6) == 0)
+				else if (g_ascii_strncasecmp(str2 + i, "<style", 6) == 0)
 				{
 					cdata_close_tag = "</style>";
 				}
@@ -3070,7 +3079,7 @@
 	i = 0; /* position in the source string */
 	j = 0; /* number of occurrences of "delimiter" */
 	while (string[i] != '\0') {
-		if (!strncasecmp(&string[i], delimiter, length_del)) {
+		if (!g_ascii_strncasecmp(&string[i], delimiter, length_del)) {
 			i += length_del;
 			j += length_rep;
 		} else {
@@ -3084,7 +3093,7 @@
 	i = 0; /* position in the source string */
 	j = 0; /* position in the destination string */
 	while (string[i] != '\0') {
-		if (!strncasecmp(&string[i], delimiter, length_del)) {
+		if (!g_ascii_strncasecmp(&string[i], delimiter, length_del)) {
 			strncpy(&ret[j], replacement, length_rep);
 			i += length_del;
 			j += length_rep;
--- a/libpurple/util.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/util.h	Sat Jul 07 03:52:12 2007 +0000
@@ -257,6 +257,16 @@
 const char *purple_utf8_strftime(const char *format, const struct tm *tm);
 
 /**
+ * Gets a string representation of the local timezone offset
+ *
+ * @param tm   The time to get the timezone for
+ * @param iso  TRUE to format the offset according to ISO-8601, FALSE to
+ *             not substitute 'Z' for 0 offset, and to not separate
+ *             hours and minutes with a colon.
+ */
+const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso);
+
+/**
  * Formats a time into the user's preferred short date format.
  *
  * The returned string is stored in a static buffer, so the result
@@ -1095,12 +1105,13 @@
 void purple_print_utf8_to_console(FILE *filestream, char *message);
 
 /**
- * Checks for messages starting with "/me "
+ * Checks for messages starting (post-HTML) with "/me ", including the space.
  *
  * @param message The message to check
  * @param len     The message length, or -1
  *
- * @return TRUE if it starts with /me, and it has been removed, otherwise FALSE
+ * @return TRUE if it starts with "/me ", and it has been removed, otherwise
+ *         FALSE
  */
 gboolean purple_message_meify(char *message, size_t len);
 
--- a/libpurple/win32/libc_interface.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/win32/libc_interface.c	Sat Jul 07 03:52:12 2007 +0000
@@ -413,8 +413,8 @@
 		/* newname exists */
 		if(g_stat(newname, &newstat) == 0) {
 			/* oldname is a dir */
-			if(_S_ISDIR(oldstat.st_mode)) {
-				if(!_S_ISDIR(newstat.st_mode)) {
+			if(S_ISDIR(oldstat.st_mode)) {
+				if(!S_ISDIR(newstat.st_mode)) {
 					return g_rename(oldname, newname);
 				}
 				/* newname is a dir */
@@ -430,7 +430,7 @@
 			/* oldname is not a dir */
 			else {
 				/* newname is a dir */
-				if(_S_ISDIR(newstat.st_mode)) {
+				if(S_ISDIR(newstat.st_mode)) {
 					errno = EISDIR;
 					return -1;
 				}
--- a/libpurple/win32/libc_interface.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/win32/libc_interface.h	Sat Jul 07 03:52:12 2007 +0000
@@ -29,6 +29,16 @@
 #include "libc_internal.h"
 #include <glib.h>
 
+#ifdef _MSC_VER
+#define S_IRUSR S_IREAD
+#define S_IWUSR S_IWRITE
+#define S_IXUSR S_IEXEC
+
+#define S_ISDIR(m)	 (((m)&S_IFDIR)==S_IFDIR)
+
+#define F_OK 0
+#endif
+
 /* sys/socket.h */
 #define socket( namespace, style, protocol ) \
 wpurple_socket( namespace, style, protocol )
@@ -36,11 +46,11 @@
 #define connect( socket, addr, length ) \
 wpurple_connect( socket, addr, length )
 
-#define getsockopt( args... ) \
-wpurple_getsockopt( args )
+#define getsockopt( socket, level, optname, optval, optlenptr ) \
+wpurple_getsockopt( socket, level, optname, optval, optlenptr )
 
-#define setsockopt( args... ) \
-wpurple_setsockopt( args )
+#define setsockopt( socket, level, optname, optval, optlen ) \
+wpurple_setsockopt( socket, level, optname, optval, optlen )
 
 #define getsockname( socket, addr, lenptr ) \
 wpurple_getsockname( socket, addr, lenptr )
--- a/libpurple/win32/win32dep.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/win32/win32dep.c	Sat Jul 07 03:52:12 2007 +0000
@@ -32,6 +32,7 @@
 #include <glib.h>
 #include <glib/gstdio.h>
 
+#include "internal.h"
 #include "debug.h"
 #include "notify.h"
 
@@ -42,8 +43,6 @@
 /*
  *  DEFINES & MACROS
  */
-#define _(x) gettext(x)
-
 #define WIN32_PROXY_REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
 
 /* For shfolder.dll */
--- a/libpurple/xmlnode.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/libpurple/xmlnode.c	Sat Jul 07 03:52:12 2007 +0000
@@ -268,6 +268,22 @@
 
 	g_return_if_fail(node != NULL);
 
+	/* if we're part of a tree, remove ourselves from the tree first */
+	if(NULL != node->parent) {
+		if(node->parent->child == node) {
+			node->parent->child = node->next;
+		} else {
+			xmlnode *prev = node->parent->child;
+			while(prev && prev->next != node) {
+				prev = prev->next;
+			}
+			if(prev) {
+				prev->next = node->next;
+			}
+		}
+	}
+
+	/* now free our children */
 	x = node->child;
 	while(x) {
 		y = x->next;
@@ -275,6 +291,7 @@
 		x = y;
 	}
 
+	/* now dispose of ourselves */
 	g_free(node->name);
 	g_free(node->data);
 	g_free(node->xmlns);
--- a/pidgin/eggtrayicon.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/eggtrayicon.c	Sat Jul 07 03:52:12 2007 +0000
@@ -152,6 +152,26 @@
     }
 }
 
+static Display *
+egg_tray_icon_get_x_display(EggTrayIcon *icon)
+{
+  Display *xdisplay = NULL;
+
+#if GTK_CHECK_VERSION(2,1,0)
+  {
+    GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon));
+    if (!GDK_IS_DISPLAY (display))
+      display = gdk_display_get_default ();
+
+    xdisplay = GDK_DISPLAY_XDISPLAY (display);
+  }
+#else
+  xdisplay = gdk_display;
+#endif
+
+  return xdisplay;
+}
+
 static void
 egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
 {
@@ -168,11 +188,10 @@
 
   g_return_if_fail(icon->manager_window != None);
 
-#if GTK_CHECK_VERSION(2,1,0)
-  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
-#else
-  xdisplay = gdk_display;
-#endif
+  xdisplay = egg_tray_icon_get_x_display(icon);
+
+  if (xdisplay == NULL)
+    return;
 
   gdk_error_trap_push ();
   type = None;
@@ -321,11 +340,10 @@
   if (icon->manager_window != None)
     return;
 
-#if GTK_CHECK_VERSION(2,1,0)
-  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
-#else
-  xdisplay = gdk_display;
-#endif
+  xdisplay = egg_tray_icon_get_x_display(icon);
+
+  if (xdisplay == NULL)
+    return;
 
   XGrabServer (xdisplay);
   
@@ -424,12 +442,15 @@
 
   make_transparent (widget, NULL);
 
+  xdisplay = egg_tray_icon_get_x_display(icon);
+
+  if (xdisplay == NULL)
+    return;
+
 #if GTK_CHECK_VERSION(2,1,0)
   screen = gdk_screen_get_number (gtk_widget_get_screen (widget));
-  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
 #else
   screen = XScreenNumberOfScreen (DefaultScreenOfDisplay (gdk_display));
-  xdisplay = gdk_display;
 #endif
 
   /* Now see if there's a manager window around */
@@ -519,11 +540,10 @@
       XClientMessageEvent ev;
       Display *xdisplay;
 
-#if GTK_CHECK_VERSION(2,1,0)
-      xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
-#else
-      xdisplay = gdk_display;
-#endif
+      xdisplay = egg_tray_icon_get_x_display(icon);
+
+      if (xdisplay == NULL)
+        return;
 
       ev.type = ClientMessage;
       ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
--- a/pidgin/gtkblist.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkblist.c	Sat Jul 07 03:52:12 2007 +0000
@@ -174,6 +174,7 @@
 
 static gboolean gtk_blist_window_state_cb(GtkWidget *w, GdkEventWindowState *event, gpointer data)
 {
+#if GTK_CHECK_VERSION(2,2,0)
 	if(event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN) {
 		if(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)
 			purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
@@ -195,6 +196,28 @@
 		if (!(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED))
 			pidgin_blist_refresh_timer(purple_get_blist());
 	}
+#else
+	/* At least gtk+ 2.0.6 does not properly set the change_mask when unsetting a
+	 * GdkWindowState flag. To work around, the window state will be explicitly
+	 * queried on these older versions of gtk+. See pidgin ticket #739.
+	 */
+	GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(gtkblist->window->window));
+
+	if(new_window_state & GDK_WINDOW_STATE_WITHDRAWN) {
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
+	} else {
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", TRUE);
+		pidgin_blist_refresh_timer(purple_get_blist());
+	}
+
+	if(new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", TRUE);
+	else
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", FALSE);
+
+	if (!(new_window_state & GDK_WINDOW_STATE_ICONIFIED))
+		pidgin_blist_refresh_timer(purple_get_blist());
+#endif
 
 	return FALSE;
 }
@@ -2850,7 +2873,11 @@
 	{ N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL },
 	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
 	{ N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL },
+#if GTK_CHECK_VERSION(2,6,0)	
+	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<StockItem>", GTK_STOCK_ABOUT },
+#else
 	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<Item>", NULL },
+#endif
 };
 
 /*********************************************************
@@ -2976,10 +3003,11 @@
 		signon = purple_presence_get_login_time(presence);
 		if (full && PURPLE_BUDDY_IS_ONLINE(b) && signon > 0)
 		{
-			if (time(NULL) - signon > 63072000 /* 2 years */) {
+			if (signon > time(NULL)) {
 				/*
-				 * Our local clock must be wrong, show the actual
-				 * date instead of "4 days", etc.
+				 * They signed on in the future?!  Our local clock
+				 * must be wrong, show the actual date instead of
+				 * "4 days", etc.
 				 */
 				tmp = g_strdup(purple_date_format_long(localtime(&signon)));
 			} else
@@ -3092,6 +3120,15 @@
 	} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		buddy = (PurpleBuddy*)node;
 		gtkbuddynode = node->ui_data;
+		p = purple_buddy_get_presence(buddy);
+		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", 
+						"16", "mobile.png", NULL);
+			ret = gdk_pixbuf_new_from_file(path, NULL);
+			g_free(path);
+			return ret;
+		}
+
 		if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded)
 			return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL);
 	} else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
@@ -3380,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);
@@ -4887,10 +4927,17 @@
 	gboolean selected;
 	char group_count[12] = "";
 	char *mark, *esc;
+	PurpleBlistNode *selected_node = NULL;
+	GtkTreeIter iter;
 
 	group = (PurpleGroup*)gnode;
 	textcolor = gtkblist->treeview->style->fg[GTK_STATE_ACTIVE];
-	selected = gtkblist ? (gtkblist->selected_node == gnode) : FALSE;
+        
+	if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
+				NODE_COLUMN, &selected_node, -1);
+	}
+	selected = (gnode == selected_node);
 
 	if (!expanded) {
 		g_snprintf(group_count, sizeof(group_count), " (%d/%d)",
--- a/pidgin/gtkconv.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkconv.c	Sat Jul 07 03:52:12 2007 +0000
@@ -256,19 +256,11 @@
 	 * negate it anyway.  --luke */
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 	{
-		if (w == gtkconv->imhtml) {
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width", allocation->width);
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height", allocation->height);
-		}
 		if (w == gtkconv->lower_hbox)
 			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height);
 	}
 	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
 	{
-		if (w == gtkconv->imhtml) {
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", allocation->width);
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", allocation->height);
-		}
 		if (w == gtkconv->lower_hbox)
 			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height);
 	}
@@ -347,15 +339,28 @@
 	}
 }
 
-static PurpleCmdRet
-clear_command_cb(PurpleConversation *conv,
-                 const char *cmd, char **args, char **error, void *data)
+static void clear_conversation_scrollback(PurpleConversation *conv)
 {
 	PidginConversation *gtkconv = NULL;
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
 	gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml));
+}
+
+static PurpleCmdRet
+clear_command_cb(PurpleConversation *conv,
+                 const char *cmd, char **args, char **error, void *data)
+{
+	clear_conversation_scrollback(conv);
+	return PURPLE_CMD_STATUS_OK;
+}
+
+static PurpleCmdRet
+clearall_command_cb(PurpleConversation *conv,
+                 const char *cmd, char **args, char **error, void *data)
+{
+	purple_conversation_foreach(clear_conversation_scrollback);
 	return PURPLE_CMD_STATUS_OK;
 }
 
@@ -528,13 +533,6 @@
 
 	account = purple_conversation_get_account(conv);
 
-	if (!purple_account_is_connected(account))
-		return;
-
-	if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
-		purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
-		return;
-
 	if (check_for_and_do_command(conv)) {
 		if (gtkconv->entry_growing) {
 			reset_default_size(gtkconv);
@@ -544,6 +542,13 @@
 		return;
 	}
 
+	if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
+		purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
+		return;
+
+	if (!purple_account_is_connected(account))
+		return;
+
 	buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
 	clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
 
@@ -1736,6 +1741,9 @@
 
 	gtk_tree_selection_select_path(GTK_TREE_SELECTION(
 			gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path);
+	gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkchat->list),
+							 path, NULL, FALSE); 
+	gtk_widget_grab_focus(GTK_WIDGET(gtkchat->list));
 
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
 	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
@@ -2398,9 +2406,6 @@
 		return FALSE;
 	}
 
-	gtkconv->auto_resize = TRUE;
-	g_idle_add(reset_auto_resize_cb, gtkconv);
-
 	gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL);
 	buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter);
 
@@ -3671,7 +3676,7 @@
 	if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name)))
 		is_me = TRUE;
 
-	is_buddy = (purple_find_buddy(conv->account, name) != NULL);
+	is_buddy = cb->buddy;
 
 	tmp = g_utf8_casefold(alias, -1);
 	alias_key = g_utf8_collate_key(tmp, -1);
@@ -3879,7 +3884,7 @@
 						   CHAT_USERS_ALIAS_COLUMN, &alias,
 						   -1);
 
-				if (strcmp(name, alias))
+				if (name && alias && strcmp(name, alias))
 					tab_complete_process_item(&most_matched, entered, &partial, nick_partial,
 										  &matches, FALSE, alias);
 				g_free(name);
@@ -4248,7 +4253,7 @@
 	if (sr.height < height + PIDGIN_HIG_BOX_SPACE) {
 		gtkconv->auto_resize = TRUE;
 		gtkconv->entry_growing = TRUE;
-	        gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE);
+	        gtk_widget_set_size_request(gtkconv->entry, -1, height);
 	        g_idle_add(reset_auto_resize_cb, gtkconv);
 	}
 }
@@ -4464,15 +4469,6 @@
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
 	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
 
-	gtk_widget_set_size_request(gtkconv->imhtml,
-			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width") :
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
-			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height") :
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
-
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
-	                 G_CALLBACK(size_allocate_cb), gtkconv);
-
 	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
 	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
@@ -5563,7 +5559,7 @@
 
 	g_return_if_fail(new_alias != NULL);
 
-	cbuddy = purple_conv_chat_cb_new(new_name, new_alias, flags);
+	cbuddy = purple_conv_chat_cb_find(chat, new_name);
 
 	add_chat_buddy_common(conv, cbuddy, old_name);
 }
@@ -6192,10 +6188,12 @@
 				markup = title;
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 			PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
-			markup = g_strdup_printf("%s\n<span color='%s' size='smaller'>%s</span>",
+			const char *topic = purple_conv_chat_get_topic(chat);
+			markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
 						purple_conversation_get_title(conv),
+						topic ? "\n" : "",
 						pidgin_get_dim_grey_string(gtkconv->infopane),
-						purple_conv_chat_get_topic(chat));
+						topic ? topic : "");
 		}
 		gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
 				TEXT_COLUMN, markup, -1);
@@ -7057,6 +7055,8 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/tabs", TRUE);
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP);
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000);
+	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/x", 0);
+	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/y", 0);
 
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font", TRUE);
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", "");
@@ -7202,6 +7202,9 @@
 	purple_cmd_register("clear", "", PURPLE_CMD_P_DEFAULT,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
 	                  clear_command_cb, _("clear: Clears the conversation scrollback."), NULL);
+	purple_cmd_register("clearall", "", PURPLE_CMD_P_DEFAULT,
+	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
+	                  clearall_command_cb, _("clear: Clears all conversation scrollbacks."), NULL);
 	purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
 	                  help_command_cb, _("help &lt;command&gt;:  Help on a specific command."), NULL);
@@ -7736,6 +7739,7 @@
 static gboolean
 infopane_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
 {
+	return FALSE;
 }
 
 static gboolean
@@ -8069,6 +8073,11 @@
 		return FALSE;
 	}
 
+	if (!purple_account_is_connected(gtkconv->active_conv->account)) {
+		/* Do not allow aliasing someone on a disconnected account. */
+		return FALSE;
+	}
+
 	/* alias label */
 	entry = gtk_entry_new();
 	gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
@@ -8197,6 +8206,44 @@
 	regenerate_plugins_items(data);
 }
 
+static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) {
+	int x, y;
+	
+	if (GTK_WIDGET_VISIBLE(w))
+		gtk_window_get_position(GTK_WINDOW(w), &x, &y);
+	else
+		return FALSE; /* carry on normally */
+
+	/* Workaround for GTK+ bug # 169811 - "configure_event" is fired
+	* when the window is being maximized */
+	if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
+		return FALSE;
+	
+	/* don't save if nothing changed */
+	if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x") &&
+	    y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y") &&
+	    event->width  == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width") &&
+	    event->height == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"))
+		return FALSE; /* carry on normally */
+		
+	/* don't save off-screen positioning */
+	if (x + event->width < 0 ||
+	    y + event->height < 0 ||
+	    x > gdk_screen_width() ||
+	    y > gdk_screen_height())
+		return FALSE; /* carry on normally */
+
+        /* store the position */
+        purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x",      x);
+	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y",      y);
+	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width",  event->width);
+	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height);
+
+	/* continue to handle event normally */
+	return FALSE;
+						
+}
+
 PidginWindow *
 pidgin_conv_window_new()
 {
@@ -8211,7 +8258,8 @@
 
 	/* Create the window. */
 	win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
-	GTK_WINDOW(win->window)->allow_shrink = TRUE;
+	gtk_window_set_default_size(GTK_WINDOW(win->window), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"),
+							     purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"));
 
 	if (available_list == NULL) {
 		create_icon_lists(win->window);
@@ -8219,7 +8267,8 @@
 
 	g_signal_connect(G_OBJECT(win->window), "delete_event",
 	                 G_CALLBACK(close_win_cb), win);
-
+	g_signal_connect(G_OBJECT(win->window), "configure_event", 
+			 G_CALLBACK(gtk_conv_configure_cb), NULL);
 	g_signal_connect(G_OBJECT(win->window), "focus_in_event",
 	                 G_CALLBACK(focus_win_cb), win);
 
@@ -8554,7 +8603,7 @@
 	win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv);
 
 	if (!win->gtkconvs || !win->gtkconvs->next)
-		gtk_notebook_set_show_tabs(win->notebook, FALSE);
+		gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
 
 	if (!win->gtkconvs && win != hidden_convwin)
 		pidgin_conv_window_destroy(win);
--- a/pidgin/gtkdialogs.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkdialogs.c	Sat Jul 07 03:52:12 2007 +0000
@@ -165,7 +165,7 @@
 	{N_("Japanese"),            "ja", "Takashi Aihana", "aihana@gnome.gr.jp"},
 	{N_("Georgian"),            "ka", N_("Ubuntu Georgian Translators"), "alexander.didebulidze@stusta.mhn.de"},
 	{N_("Kannada"),             "kn", N_("Kannada Translation team"), "translation@sampada.info"},
-	{N_("Korean"),              "ko", "Kyung-uk Son", "vvs740@chol.com"},
+	{N_("Korean"),              "ko", "Sushizang", "sushizang@empal.com"},
 	{N_("Kurdish"),             "ku", "Erdal Ronahi", "erdal.ronahi@gmail.com"},
 	{N_("Kurdish"),             "ku", "Amed Ç. Jiyan", "amedcj@hotmail.com"},
 	{N_("Kurdish"),             "ku", "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
@@ -230,6 +230,7 @@
 	{N_("Japanese"),            "ja", "Junichi Uekawa", NULL},
 	{N_("Georgian"),            "ka", "Temuri Doghonadze", NULL},
 	{N_("Korean"),              "ko", "Sang-hyun S, A Ho-seok Lee", NULL},
+	{N_("Korean"),              "ko", "Kyeong-uk Son", "hey@gnome.or.kr"},
 	{N_("Lithuanian"),          "lt", "Andrius Štikonas", NULL},
 	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Macedonian"),          "mk", "Tomislav Markovski", "herrera@users.sf.net"},
--- a/pidgin/gtkdocklet.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkdocklet.c	Sat Jul 07 03:52:12 2007 +0000
@@ -636,7 +636,7 @@
 
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE);
-	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "pending");
 	purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show",
 				    docklet_show_pref_changed_cb, NULL);
 
--- a/pidgin/gtkimhtml.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkimhtml.c	Sat Jul 07 03:52:12 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"
@@ -494,6 +494,8 @@
 	GSList *tags = NULL, *templist = NULL;
 	GdkColor *norm, *pre;
 	GtkTextTag *tag = NULL, *oldprelit_tag;
+	GtkTextChildAnchor* anchor;
+	gboolean hand = TRUE;
 
 	oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag;
 
@@ -551,8 +553,15 @@
 		GTK_IMHTML(imhtml)->tip_timer = 0;
 	}
 
+	/* If we don't have a tip from a URL, let's see if we have a tip from a smiley */
+	anchor = gtk_text_iter_get_child_anchor(&iter);
+	if (anchor) {
+		tip = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
+		hand = FALSE;
+	}
+
 	if (tip){
-		if (!GTK_IMHTML(imhtml)->editable)
+		if (!GTK_IMHTML(imhtml)->editable && hand)
 			gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
 		GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
 							       gtk_imhtml_tip, imhtml);
@@ -1002,11 +1011,12 @@
 		gtk_imhtml_close_tags(imhtml, &iter);
 
 	gtk_imhtml_insert_html_at_iter(imhtml, text, flags, &iter);
-	if (!imhtml->wbfo && !plaintext)
-		gtk_imhtml_close_tags(imhtml, &iter);
 	gtk_text_buffer_move_mark_by_name(imhtml->text_buffer, "insert", &iter);
 	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(imhtml), gtk_text_buffer_get_insert(imhtml->text_buffer),
 	                             0, FALSE, 0.0, 0.0);
+	if (!imhtml->wbfo && !plaintext)
+		gtk_imhtml_close_tags(imhtml, &iter);
+
 }
 
 static void paste_plaintext_received_cb (GtkClipboard *clipboard, const gchar *text, gpointer data)
@@ -2152,7 +2162,7 @@
 	gint i;
 
 	for(i=0; i<accepted_protocols_size; i++){
-		if( strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0  ){
+		if( g_ascii_strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0  ){
 			return strlen(accepted_protocols[i]);
 		}
 	}
@@ -2725,7 +2735,7 @@
 						/* NEW_BIT (NEW_TEXT_BIT); */
 
 						/* Bi-Directional text support */
-						if (direction && (!strncasecmp(direction, "RTL", 3))) {
+						if (direction && (!g_ascii_strncasecmp(direction, "RTL", 3))) {
 							rtl_direction = TRUE;
 							/* insert RLE character to set direction */
 							ws[wpos++]  = 0xE2;
@@ -2737,7 +2747,7 @@
 						}
 						g_free(direction);
 
-						if (alignment && (!strncasecmp(alignment, "RIGHT", 5))) {
+						if (alignment && (!g_ascii_strncasecmp(alignment, "RIGHT", 5))) {
 							align_right = TRUE;
 							align_line = gtk_text_iter_get_line(iter);
 						}
@@ -4620,6 +4630,7 @@
 			GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
 			gtk_container_add(GTK_CONTAINER(ebox), img);
 			gtk_widget_show(img);
+			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free);
 			gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox, anchor);
 		}
 	} else {
@@ -4938,9 +4949,6 @@
 
 	if (imhtml->edit.link)
 		gtk_imhtml_toggle_link(imhtml, NULL);
-
-	gtk_text_buffer_remove_all_tags(imhtml->text_buffer, iter, iter);
-
 }
 
 char *gtk_imhtml_get_markup(GtkIMHtml *imhtml)
--- a/pidgin/gtkimhtmltoolbar.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sat Jul 07 03:52:12 2007 +0000
@@ -37,6 +37,8 @@
 #include "gtkthemes.h"
 #include "gtkutils.h"
 
+#include <gdk/gdkkeysyms.h>
+
 static GtkHBoxClass *parent_class = NULL;
 
 static void toggle_button_set_active_block(GtkToggleButton *button,
@@ -629,7 +631,6 @@
 	return ls;
 }
 
-
 static gboolean
 smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley)
 {
@@ -642,6 +643,18 @@
 	return TRUE;
 }
 
+static gboolean
+smiley_dialog_input_cb(GtkWidget *dialog, GdkEvent *event, GtkIMHtmlToolbar *toolbar)
+{
+	if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_Escape) ||
+	    (event->type == GDK_BUTTON_PRESS && event->button.button == 1))
+	{
+		close_smiley_dialog(NULL, NULL, toolbar);
+		return TRUE;
+	}
+
+	return FALSE;
+}
 
 static void
 insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
@@ -720,11 +733,16 @@
 			free(it_tmp);
 		}
 		gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, TRUE, 0);
+
+		gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK);
 	}
 	else {
 		smiley_table = gtk_label_new(_("This theme has no available smileys."));
+		gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
+		g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
 	}
 
+	g_signal_connect(G_OBJECT(dialog), "key-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
 	gtk_container_add(GTK_CONTAINER(dialog), smiley_table);
 
 	gtk_widget_show(smiley_table);
--- a/pidgin/gtkmain.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkmain.c	Sat Jul 07 03:52:12 2007 +0000
@@ -677,6 +677,8 @@
 		return 1;
 	}
 
+	g_set_application_name(_("Pidgin"));
+
 #ifdef _WIN32
 	winpidgin_init(hint);
 #endif
@@ -855,7 +857,6 @@
 	winpidgin_post_init();
 #endif
 
-	g_set_application_name(_("Pidgin"));
 	gtk_main();
 
 #ifdef HAVE_SIGNAL_H
--- a/pidgin/gtkprefs.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkprefs.c	Sat Jul 07 03:52:12 2007 +0000
@@ -60,6 +60,7 @@
 
 static GtkWidget *sound_entry = NULL;
 static GtkListStore *smiley_theme_store = NULL;
+static GtkTreeSelection *smiley_theme_sel = NULL;
 static GtkWidget *prefs_proxy_frame = NULL;
 
 static GtkWidget *prefs = NULL;
@@ -366,9 +367,12 @@
 	GValue val;
 	GtkTreePath *path, *oldpath;
 	struct smiley_theme *new_theme, *old_theme;
+	GtkWidget *remove_button = g_object_get_data(G_OBJECT(sel), "remove_button");
 
-	if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+	if (!gtk_tree_selection_get_selected(sel, &model, &iter)) {
+		gtk_widget_set_sensitive(remove_button, FALSE);
 		return;
+	}
 
 	old_theme = current_smiley_theme;
 	val.g_type = 0;
@@ -376,6 +380,9 @@
 	path = gtk_tree_model_get_path(model, &iter);
 	themename = g_value_get_string(&val);
 	purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", themename);
+
+	gtk_widget_set_sensitive(remove_button, (strcmp(themename, "none") &&
+	                                         strcmp(themename, _("Default"))));
 	g_value_unset (&val);
 
 	/* current_smiley_theme is set in callback for the above pref change */
@@ -514,8 +521,13 @@
 	g_free(destdir);
 
 	theme_rowref = theme_refresh_theme_list();
-	if (theme_rowref != NULL)
+	if (theme_rowref != NULL) {
+		GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref);
+
+		if (tp)
+			gtk_tree_selection_select_path(smiley_theme_sel, tp);
 		gtk_tree_row_reference_free(theme_rowref);
+	}
 }
 
 static void
@@ -619,9 +631,55 @@
 	return ret;
 }
 
+static void
+request_theme_file_name_cb(gpointer data, char *theme_file_name)
+{
+	theme_install_theme(theme_file_name, NULL) ;
+}
+
+static void
+add_theme_button_clicked_cb(GtkWidget *widget, gpointer null)
+{
+	purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ;
+}
+
+static void
+remove_theme_button_clicked_cb(GtkWidget *button, GtkTreeView *tv)
+{
+	char *theme_name = NULL, *theme_file = NULL;
+	GtkTreeModel *tm;
+	GtkTreeIter itr;
+	GtkTreeRowReference *trr = NULL;
+
+	if ((tm = gtk_tree_view_get_model(tv)) == NULL)
+		return;
+	if (!gtk_tree_selection_get_selected(smiley_theme_sel, NULL, &itr))
+		return;
+	gtk_tree_model_get(tm, &itr, 2, &theme_file, 3, &theme_name, -1);
+
+	if (theme_file && theme_name && strcmp(theme_name, "none"))
+		pidgin_themes_remove_smiley_theme(theme_file);
+
+	if ((trr = theme_refresh_theme_list()) != NULL) {
+		GtkTreePath *tp = gtk_tree_row_reference_get_path(trr);
+
+		if (tp) {
+			gtk_tree_selection_select_path(smiley_theme_sel, tp);
+			gtk_tree_path_free(tp);
+		}
+		gtk_tree_row_reference_free(trr);
+	}
+
+	g_free(theme_file);
+	g_free(theme_name);
+}
+
 static GtkWidget *
 theme_page()
 {
+	GtkWidget *add_button, *remove_button;
+	GtkWidget *hbox_buttons;
+	GtkWidget *alignment;
 	GtkWidget *ret;
 	GtkWidget *sw;
 	GtkWidget *view;
@@ -661,7 +719,7 @@
 	g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), smiley_theme_store);
 
 	rend = gtk_cell_renderer_pixbuf_new();
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+	smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
 
 	/* Custom sort so "none" theme is at top of list */
 	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(smiley_theme_store),
@@ -687,6 +745,25 @@
 
 	g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smiley_sel), NULL);
 
+	alignment = gtk_alignment_new(1.0, 0.5, 0.0, 1.0);
+	gtk_widget_show(alignment);
+	gtk_box_pack_start(GTK_BOX(ret), alignment, FALSE, TRUE, 0);
+
+	hbox_buttons = gtk_hbox_new(TRUE, PIDGIN_HIG_CAT_SPACE);
+	gtk_widget_show(hbox_buttons);
+	gtk_container_add(GTK_CONTAINER(alignment), hbox_buttons);
+
+	add_button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	gtk_widget_show(add_button);
+	gtk_box_pack_start(GTK_BOX(hbox_buttons), add_button, FALSE, TRUE, 0);
+	g_signal_connect(G_OBJECT(add_button), "clicked", (GCallback)add_theme_button_clicked_cb, view);
+
+	remove_button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+	gtk_widget_show(remove_button);
+	gtk_box_pack_start(GTK_BOX(hbox_buttons), remove_button, FALSE, TRUE, 0);
+	g_signal_connect(G_OBJECT(remove_button), "clicked", (GCallback)remove_theme_button_clicked_cb, view);
+	g_object_set_data(G_OBJECT(sel), "remove_button", remove_button);
+
 	if (rowref) {
 		GtkTreePath *path = gtk_tree_row_reference_get_path(rowref);
 		gtk_tree_row_reference_free(rowref);
@@ -975,7 +1052,7 @@
 									GTK_IMHTML_BACKCOLOR |
 									GTK_IMHTML_BACKGROUND);
 
-	gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting. :)"), 0);
+	gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting."), 0);
 
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 
--- a/pidgin/gtkprivacy.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkprivacy.c	Sat Jul 07 03:52:12 2007 +0000
@@ -366,7 +366,7 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", FALSE);
+	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
--- a/pidgin/gtksavedstatuses.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Sat Jul 07 03:52:12 2007 +0000
@@ -658,17 +658,16 @@
 					 G_CALLBACK(status_window_close_cb), dialog);
 
 	purple_signal_connect(purple_savedstatuses_get_handle(),
-			"savedstatus-changed", dialog,
+			"savedstatus-changed", status_window,
 			PURPLE_CALLBACK(current_status_changed), dialog);
-
 	purple_signal_connect(purple_savedstatuses_get_handle(),
-			"savedstatus-added", dialog,
+			"savedstatus-added", status_window,
 			PURPLE_CALLBACK(saved_status_updated_cb), dialog);
 	purple_signal_connect(purple_savedstatuses_get_handle(),
-			"savedstatus-deleted", dialog,
+			"savedstatus-deleted", status_window,
 			PURPLE_CALLBACK(saved_status_updated_cb), dialog);
 	purple_signal_connect(purple_savedstatuses_get_handle(),
-			"savedstatus-modified", dialog,
+			"savedstatus-modified", status_window,
 			PURPLE_CALLBACK(saved_status_updated_cb), dialog);
 
 	gtk_widget_show_all(win);
@@ -685,6 +684,7 @@
 
 	purple_request_close_with_handle(status_window);
 	purple_notify_close_with_handle(status_window);
+	purple_signals_disconnect_by_handle(status_window);
 	g_free(status_window);
 	status_window = NULL;
 }
@@ -1140,7 +1140,7 @@
 	if (edit)
 		dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
 
-	dialog->window = win = pidgin_create_window (_("Status"), PIDGIN_HIG_BORDER, "status", FALSE) ;
+	dialog->window = win = pidgin_create_window(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
@@ -1475,7 +1475,7 @@
 	dialog->account = account;
 
 	tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
-	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", FALSE) ;
+	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
 	g_free(tmp);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1520,7 +1520,7 @@
 
 	/* Status mesage */
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
 
 	label = gtk_label_new_with_mnemonic(_("_Message:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
--- a/pidgin/gtkstatusbox.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Sat Jul 07 03:52:12 2007 +0000
@@ -1016,8 +1016,8 @@
 	}
 }
 
-static void
-pidgin_status_box_regenerate(PidginStatusBox *status_box)
+static gboolean
+pidgin_status_box_regenerate_real(PidginStatusBox *status_box)
 {
 	GdkPixbuf *pixbuf, *pixbuf2, *pixbuf3, *pixbuf4, *pixbuf5;
 	GtkIconSize icon_size;
@@ -1081,11 +1081,21 @@
 	}
 	gtk_tree_view_set_model(GTK_TREE_VIEW(status_box->tree_view), GTK_TREE_MODEL(status_box->dropdown_store));
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN);
+
+	return FALSE;
+}
+
+static void
+pidgin_status_box_regenerate(PidginStatusBox *status_box)
+{
+	/* we have to do this in a timeout, so we avoid recursing
+	 * to infinity (and beyond) */
+	purple_timeout_add(0, (GSourceFunc)pidgin_status_box_regenerate_real, status_box);
 }
 
 static gboolean combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
 {
-  	pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
+	pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
 	return TRUE;
 }
 
--- a/pidgin/gtkthemes.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkthemes.c	Sat Jul 07 03:52:12 2007 +0000
@@ -36,6 +36,8 @@
 GSList *smiley_themes = NULL;
 struct smiley_theme *current_smiley_theme;
 
+static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme);
+
 gboolean pidgin_themes_smileys_disabled()
 {
 	if (!current_smiley_theme)
@@ -44,6 +46,79 @@
 	return strcmp(current_smiley_theme->name, "none") == 0;
 }
 
+static void
+pidgin_themes_destroy_smiley_theme(struct smiley_theme *theme)
+{
+	pidgin_themes_destroy_smiley_theme_smileys(theme);
+
+	g_free(theme->name);
+	g_free(theme->desc);
+	g_free(theme->author);
+	g_free(theme->icon);
+	g_free(theme->path);
+	g_free(theme);
+}
+
+static void pidgin_themes_remove_theme_dir(const char *theme_dir_name)
+{
+	GString *str = NULL;
+	const char *file_name = NULL;
+	GDir *theme_dir = NULL;
+
+	if ((theme_dir = g_dir_open(theme_dir_name, 0, NULL)) != NULL) {
+		if ((str = g_string_new(theme_dir_name)) != NULL) {
+			while ((file_name = g_dir_read_name(theme_dir)) != NULL) {
+				g_string_printf(str, "%s%s%s", theme_dir_name, G_DIR_SEPARATOR_S, file_name);
+				g_unlink(str->str);
+			}
+			g_string_free(str, TRUE);
+		}
+		g_dir_close(theme_dir);
+		g_rmdir(theme_dir_name);
+	}
+}
+
+void pidgin_themes_remove_smiley_theme(const char *file)
+{
+	char *theme_dir = NULL, *last_slash = NULL;
+	g_return_if_fail(NULL != file);
+	
+	if (!g_file_test(file, G_FILE_TEST_EXISTS)) return;
+	if ((theme_dir = g_strdup(file)) == NULL) return ;
+
+	if ((last_slash = g_strrstr(theme_dir, G_DIR_SEPARATOR_S)) != NULL) {
+		GSList *iter = NULL;
+		struct smiley_theme *theme = NULL, *new_theme = NULL;
+
+		*last_slash = 0;
+
+		/* Delete files on disk */
+		pidgin_themes_remove_theme_dir(theme_dir);
+
+		/* Find theme in themes list and remove it */
+		for (iter = smiley_themes ; iter ; iter = iter->next) {
+			theme = ((struct smiley_theme *)(iter->data));
+			if (!strcmp(theme->path, file))
+				break ;
+		}
+		if (iter) {
+			if (theme == current_smiley_theme) {
+				new_theme = ((struct smiley_theme *)(NULL == iter->next ? (smiley_themes == iter ? NULL : smiley_themes->data) : iter->next->data));
+				if (new_theme)
+					purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme->name);
+				else
+					current_smiley_theme = NULL;
+			}
+			smiley_themes = g_slist_delete_link(smiley_themes, iter);
+
+			/* Destroy theme structure */
+			pidgin_themes_destroy_smiley_theme(theme);
+		}
+	}
+
+	g_free(theme_dir);
+}
+
 void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
 {
 	struct smiley_list *list;
@@ -64,7 +139,7 @@
 }
 
 static void
-pidgin_themes_destroy_smiley_theme(struct smiley_theme *theme)
+pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme)
 {
 	GHashTable *already_freed;
 	struct smiley_list *wer;
@@ -92,6 +167,32 @@
 	g_hash_table_destroy(already_freed);
 }
 
+static void
+pidgin_smiley_themes_remove_non_existing()
+{
+	static struct smiley_theme *theme = NULL;
+	GSList *iter = NULL;
+
+	if (!smiley_themes) return ;
+
+	for (iter = smiley_themes ; iter ; iter = iter->next) {
+		theme = ((struct smiley_theme *)(iter->data));
+		if (!g_file_test(theme->path, G_FILE_TEST_EXISTS)) {
+			if (theme == current_smiley_theme)
+				current_smiley_theme = ((struct smiley_theme *)(NULL == iter->next ? NULL : iter->next->data));
+			pidgin_themes_destroy_smiley_theme(theme);
+			iter->data = NULL;
+		}
+	}
+	/* Remove all elements whose data is NULL */
+	smiley_themes = g_slist_remove_all(smiley_themes, NULL);
+
+	if (!current_smiley_theme && smiley_themes) {
+		struct smiley_theme *smile = g_slist_last(smiley_themes)->data;
+		pidgin_themes_load_smiley_theme(smile->path, TRUE);
+	}
+}
+
 void pidgin_themes_load_smiley_theme(const char *file, gboolean load)
 {
 	FILE *f = g_fopen(file, "r");
@@ -221,14 +322,6 @@
 		purple_debug_error("gtkthemes", "Invalid file format, not loading smiley theme from '%s'\n", file);
 
 		pidgin_themes_destroy_smiley_theme(theme);
-
-		g_free(theme->name);
-		g_free(theme->desc);
-		g_free(theme->author);
-		g_free(theme->icon);
-		g_free(theme->path);
-		g_free(theme);
-
 		return;
 	}
 
@@ -240,7 +333,7 @@
 		GList *cnv;
 
 		if (current_smiley_theme)
-			pidgin_themes_destroy_smiley_theme(current_smiley_theme);
+			pidgin_themes_destroy_smiley_theme_smileys(current_smiley_theme);
 		current_smiley_theme = theme;
 
 		for (cnv = purple_get_conversations(); cnv != NULL; cnv = cnv->next) {
@@ -258,10 +351,12 @@
 {
 	GDir *dir;
 	const gchar *file;
-	gchar *path;
+	gchar *path, *test_path;
 	int l;
+	char* probedirs[3];
 
-	char* probedirs[3];
+	pidgin_smiley_themes_remove_non_existing();
+
 	probedirs[0] = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", NULL);
 	probedirs[1] = g_build_filename(purple_user_dir(), "smileys", NULL);
 	probedirs[2] = 0;
@@ -269,14 +364,18 @@
 		dir = g_dir_open(probedirs[l], 0, NULL);
 		if (dir) {
 			while ((file = g_dir_read_name(dir))) {
-				path = g_build_filename(probedirs[l], file, "theme", NULL);
+				test_path = g_build_filename(probedirs[l], file, NULL);
+				if (g_file_test(test_path, G_FILE_TEST_IS_DIR)) {
+					path = g_build_filename(probedirs[l], file, "theme", NULL);
 
-				/* Here we check to see that the theme has proper syntax.
-				 * We set the second argument to FALSE so that it doesn't load
-				 * the theme yet.
-				 */
-				pidgin_themes_load_smiley_theme(path, FALSE);
-				g_free(path);
+					/* Here we check to see that the theme has proper syntax.
+					 * We set the second argument to FALSE so that it doesn't load
+					 * the theme yet.
+					 */
+					pidgin_themes_load_smiley_theme(path, FALSE);
+					g_free(path);
+				}
+				g_free(test_path);
 			}
 			g_dir_close(dir);
 		} else if (l == 1) {
@@ -284,6 +383,11 @@
 		}
 		g_free(probedirs[l]);
 	}
+
+	if (!current_smiley_theme && smiley_themes) {
+		struct smiley_theme *smile = smiley_themes->data;
+		pidgin_themes_load_smiley_theme(smile->path, TRUE);
+	}
 }
 
 GSList *pidgin_themes_get_proto_smileys(const char *id) {
@@ -333,5 +437,4 @@
 		struct smiley_theme *smile = smiley_themes->data;
 		pidgin_themes_load_smiley_theme(smile->path, TRUE);
 	}
-
 }
--- a/pidgin/gtkthemes.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkthemes.h	Sat Jul 07 03:52:12 2007 +0000
@@ -49,5 +49,6 @@
 void pidgin_themes_smiley_themeize(GtkWidget *);
 void pidgin_themes_smiley_theme_probe(void);
 void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
+void pidgin_themes_remove_smiley_theme(const char *file);
 GSList *pidgin_themes_get_proto_smileys(const char *id);
 #endif /* _PIDGINDIALOGS_H_ */
--- a/pidgin/gtkutils.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/gtkutils.c	Sat Jul 07 03:52:12 2007 +0000
@@ -176,7 +176,7 @@
 
 	sw = gtk_scrolled_window_new(NULL, NULL);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
-								   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+								   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
@@ -1147,6 +1147,9 @@
 	acc = gtk_widget_get_accessible (w);
 	label = gtk_widget_get_accessible (l);
 
+	/* Make sure mnemonics work */
+        gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
+	
 	/* If this object has no name, set it's name with the label text */
 	existing_name = atk_object_get_name (acc);
 	if (!existing_name) {
--- a/pidgin/pidginstock.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/pidginstock.c	Sat Jul 07 03:52:12 2007 +0000
@@ -169,10 +169,8 @@
 	{ PIDGIN_STOCK_TRAY_XA, "tray", "tray-extended-away.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TRAY_OFFLINE, "tray", "tray-offline.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TRAY_CONNECT, "tray", "tray-connecting.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
-/* Uncomment me after 2.0.2!
- *	{ PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
- */
+	{ PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
 };
 
 static gchar *
--- a/pidgin/pidginstock.h	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/pidginstock.h	Sat Jul 07 03:52:12 2007 +0000
@@ -137,9 +137,7 @@
 #define PIDGIN_STOCK_TRAY_OFFLINE         "pidgin-tray-offline"
 #define PIDGIN_STOCK_TRAY_CONNECT         "pidgin-tray-connect"
 #define PIDGIN_STOCK_TRAY_PENDING         "pidgin-tray-pending"
-/* Uncomment me after 2.0.2! 
- * #define PIDGIN_STOCK_TRAY_EMAIL		  "pidgin-tray-email"
- */
+#define PIDGIN_STOCK_TRAY_EMAIL		  "pidgin-tray-email"
 
 
 /*@}*/
--- a/pidgin/plugins/gtkbuddynote.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/plugins/gtkbuddynote.c	Sat Jul 07 03:52:12 2007 +0000
@@ -30,8 +30,8 @@
 	if (full) {
 		const gchar *note = purple_blist_node_get_string(node, "notes");
 
-		if (note != NULL) {
-			g_string_append_printf(text, _("\nBuddy Note: %s"),
+		if ((note != NULL) && (*note != '\0')) {
+			g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"),
 			                       note);
 		}
 	}
@@ -94,6 +94,7 @@
 check_for_buddynote(gpointer data)
 {
 	PurplePlugin *buddynote = NULL;
+	PurplePlugin *plugin = (PurplePlugin *)data;
 
 	buddynote = purple_plugins_find_with_id("core-plugin_pack-buddynote");
 
@@ -108,6 +109,18 @@
 
 		info.dependencies = g_list_append(info.dependencies,
 		                                  "core-plugin_pack-buddynote");
+
+		/* If non-gtk buddy note plugin is loaded, but we are not, then load
+		 * ourselves, otherwise people upgrading from pre-gtkbuddynote days
+		 * will not have 'Buddy Notes' showing as loaded in the plugins list.
+		 * We also trigger a save on the list of plugins because it's not been
+		 * loaded through the UI. */
+		if (purple_plugin_is_loaded(buddynote) &&
+		    !purple_plugin_is_loaded(plugin)) {
+			purple_plugin_load(plugin);
+			pidgin_plugins_save();
+		}
+
 	} else {
 		info.flags = PURPLE_PLUGIN_FLAG_INVISIBLE;
 	}
--- a/pidgin/plugins/perl/common/Makefile.mingw	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/plugins/perl/common/Makefile.mingw	Sat Jul 07 03:52:12 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/plugins/spellchk.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/plugins/spellchk.c	Sat Jul 07 03:52:12 2007 +0000
@@ -45,9 +45,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#ifndef _WIN32
-#include <strings.h>
-#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -1787,19 +1784,19 @@
 
 	while (buf_get_line(ibuf, &buf, &pnt, size)) {
 		if (*buf != '#') {
-			if (!strncasecmp(buf, "BAD ", 4))
+			if (!g_ascii_strncasecmp(buf, "BAD ", 4))
 			{
 				strncpy(bad, buf + 4, 81);
 			}
-			else if(!strncasecmp(buf, "CASE ", 5))
+			else if(!g_ascii_strncasecmp(buf, "CASE ", 5))
 			{
 				case_sensitive = *(buf+5) == '0' ? FALSE : TRUE;
 			}
-			else if(!strncasecmp(buf, "COMPLETE ", 9))
+			else if(!g_ascii_strncasecmp(buf, "COMPLETE ", 9))
 			{
 				complete = *(buf+9) == '0' ? FALSE : TRUE;
 			}
-			else if (!strncasecmp(buf, "GOOD ", 5))
+			else if (!g_ascii_strncasecmp(buf, "GOOD ", 5))
 			{
 				strncpy(good, buf + 5, 255);
 
--- a/pidgin/win32/gtkwin32dep.c	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Sat Jul 07 03:52:12 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	Thu Jul 05 06:48:38 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.h	Sat Jul 07 03:52:12 2007 +0000
@@ -45,5 +45,5 @@
 void winpidgin_post_init(void);
 void winpidgin_cleanup(void);
 
-#endif /* _WIN32DEP_H_ */
+#endif /* _GTKWIN32DEP_H_ */
 
--- a/po/ChangeLog	Thu Jul 05 06:48:38 2007 +0000
+++ b/po/ChangeLog	Sat Jul 07 03:52:12 2007 +0000
@@ -1,5 +1,8 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.1.0
+	* Korean translation updated (Sushizang)
+
 version 2.0.2
 	* Chinese (Traditional) translation updated (Ambrose C. Li)
 	* English (British) translation updated (Luke Ross)
--- a/po/ko.po	Thu Jul 05 06:48:38 2007 +0000
+++ b/po/ko.po	Sat Jul 07 03:52:12 2007 +0000
@@ -11,7 +11,7 @@
 "Project-Id-Version: gaim 2.0\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2005-10-27 16:12-0400\n"
-"PO-Revision-Date: 2007-05-07 09:18+0900\n"
+"PO-Revision-Date: 2007-06-28 10:48+0900\n"
 "Last-Translator: sushizang <sushizang@empal.com>\n"
 "Language-Team: GTP Korean <gnome-kr-translation@gnome.or.kr>\n"
 "MIME-Version: 1.0\n"
@@ -1388,18 +1388,34 @@
 msgid "Login Options"
 msgstr "로그인 설정"
 
-#: ../gtk/gtkaccount.c:492
-msgid "Local alias:"
-msgstr "별칭:"
+#: ../pidgin/gtkaccount.c:430
+msgid "Pro_tocol:"
+msgstr "프로토콜(_t):"
+
+#: ../pidgin/gtkaccount.c:441
+msgid "Screen _name:"
+msgstr "아이디(_n):"
+
+#: ../pidgin/gtkaccount.c:531
+msgid "_Local alias:"
+msgstr "별칭(_L):"
+
+#: ../pidgin/gtkaccount.c:535
+msgid "Remember pass_word"
+msgstr "비밀번호 저장"
 
 #. Build the user options frame.
 #: ../gtk/gtkaccount.c:554
 msgid "User Options"
 msgstr "사용자 설정"
 
+#: ../pidgin/gtkaccount.c:606
+msgid "New _mail notifications"
+msgstr "새 메일 알림"
+
 #. Buddy icon
-#: ../gtk/gtkaccount.c:572
-msgid "Use this buddy icon for this account:"
+#: ../pidgin/gtkaccount.c:611
+msgid "Use this buddy _icon for this account:"
 msgstr "다음 아이콘 사용:"
 
 #. Build the protocol options frame.
@@ -3906,6 +3922,22 @@
 msgid "F_lash window when IMs are received"
 msgstr "메시지를 받으면 창을 깜빡거림(_L)"
 
+#: ../pidgin/gtkprefs.c:943
+msgid "Font"
+msgstr "글꼴"
+
+#: ../pidgin/gtkprefs.c:945
+msgid "Use document font from _theme"
+msgstr "테마로부터 문서 글꼴 사용(_t)"
+
+#: ../pidgin/gtkprefs.c:947
+msgid "Use font from _theme"
+msgstr "테마로부터 글꼴 사용(_t)"
+
+#: ../pidgin/gtkprefs.c:949
+msgid "Conversation _font:"
+msgstr "대화창 글꼴(_f):"
+
 #: ../gtk/gtkprefs.c:925
 msgid "Default Formatting"
 msgstr "기본 서식"
@@ -4710,7 +4742,7 @@
 #. Not used yet.
 #: ../gtk/plugins/contact_priority.c:66
 msgid "Buddy is mobile"
-msgstr "이동 중인 친구"
+msgstr "휴대 기기 친구"
 
 #: ../gtk/plugins/contact_priority.c:68
 msgid "Buddy is offline"
@@ -9352,7 +9384,7 @@
 
 #: ../libgaim/protocols/msn/msn.c:291
 msgid "Set your mobile phone number."
-msgstr "이동 전화번호를 설정해 주십시오."
+msgstr "휴대 전화번호를 설정해 주십시오."
 
 #: ../libgaim/protocols/msn/msn.c:304
 msgid "Allow MSN Mobile pages?"
@@ -9360,7 +9392,7 @@
 
 #: ../libgaim/protocols/msn/msn.c:305
 msgid "Do you want to allow or disallow people on your buddy list to send you MSN Mobile pages to your cell phone or other mobile device?"
-msgstr "친구가 휴대 전화 등의 모바일 기기에 MSN 모바일 호출을 전송하는 것을 허가하시겠습니까?"
+msgstr "친구가 휴대 전화 등의 휴대용 기기에 MSN 모바일 호출을 전송하는 것을 허가하시겠습니까?"
 
 #: ../libgaim/protocols/msn/msn.c:309
 msgid "Allow"
@@ -9434,11 +9466,11 @@
 
 #: ../libgaim/protocols/msn/msn.c:638
 msgid "Enable/Disable Mobile Devices..."
-msgstr "모바일 기기 사용/사용 안 함..."
+msgstr "휴대용 기기 사용/사용 안 함..."
 
 #: ../libgaim/protocols/msn/msn.c:643
 msgid "Allow/Disallow Mobile Pages..."
-msgstr "모바일 호출 허가/허가 안 함..."
+msgstr "무선 호출 허가/허가 안 함..."
 
 #: ../libgaim/protocols/msn/msn.c:653
 msgid "Open Hotmail Inbox"
@@ -9446,7 +9478,7 @@
 
 #: ../libgaim/protocols/msn/msn.c:677
 msgid "Send to Mobile"
-msgstr "모바일 기기에 전송"
+msgstr "휴대용 기기에 전송"
 
 #: ../libgaim/protocols/msn/msn.c:687
 #: ../libgaim/protocols/novell/novell.c:3443
@@ -9563,11 +9595,11 @@
 
 #: ../libgaim/protocols/msn/msn.c:1602
 msgid "Home Phone"
-msgstr "자택 전화1"
+msgstr "자택 전화"
 
 #: ../libgaim/protocols/msn/msn.c:1603
 msgid "Home Phone 2"
-msgstr "자택 전화2"
+msgstr "자택 전화 2"
 
 #: ../libgaim/protocols/msn/msn.c:1604
 #: ../libgaim/protocols/oscar/oscar.c:3768
@@ -9576,7 +9608,7 @@
 
 #: ../libgaim/protocols/msn/msn.c:1605
 msgid "Personal Mobile"
-msgstr "휴대 전화"
+msgstr "개인 휴대 기기"
 
 #: ../libgaim/protocols/msn/msn.c:1606
 msgid "Home Fax"
@@ -9620,11 +9652,11 @@
 
 #: ../libgaim/protocols/msn/msn.c:1632
 msgid "Work Phone"
-msgstr "회사 전화1"
+msgstr "회사 전화"
 
 #: ../libgaim/protocols/msn/msn.c:1633
 msgid "Work Phone 2"
-msgstr "회사 전화2"
+msgstr "회사 전화 2"
 
 #: ../libgaim/protocols/msn/msn.c:1634
 #: ../libgaim/protocols/oscar/oscar.c:3781
@@ -9633,7 +9665,7 @@
 
 #: ../libgaim/protocols/msn/msn.c:1635
 msgid "Work Mobile"
-msgstr "회사 휴대 전화"
+msgstr "업무 휴대 기기"
 
 #: ../libgaim/protocols/msn/msn.c:1636
 msgid "Work Pager"
@@ -14816,7 +14848,7 @@
 
 #: ../libgaim/status.c:160
 msgid "Mobile"
-msgstr "이동 중"
+msgstr "휴대 기기"
 
 #: ../libgaim/status.c:611
 #, c-format