changeset 20509:642f08c49042

explicit merge of 'b9e805b43e8543af0b4800e2e5553c85d947c610' and '85472e3c3dd9b1f401f415ad82bf18dd5a2f15a5'
author Richard Laager <rlaager@wiktel.com>
date Sun, 16 Sep 2007 17:58:40 +0000
parents 34abe3faeaab (current diff) 1b95e3bd5628 (diff)
children b77adb803eca
files COPYRIGHT libpurple/internal.h libpurple/protocols/msn/msn-utils.c libpurple/protocols/msn/msn-utils.h libpurple/protocols/msn/slp.c libpurple/util.c
diffstat 11 files changed, 333 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Sep 16 17:52:49 2007 +0000
+++ b/COPYRIGHT	Sun Sep 16 17:58:40 2007 +0000
@@ -339,6 +339,7 @@
 Michael Shkutkov
 Ettore Simone
 John Silvestri
+Ankit Singla
 Craig Slusher
 Alex Smith
 Brad Smith
@@ -349,6 +350,7 @@
 Sony Computer Entertainment America, Inc.
 Andy Spencer
 Mark Spencer
+Peter Speybrouck
 Lex Spoon
 Chris Stafford
 Kevin Stange
@@ -375,6 +377,7 @@
 Warren Togami
 Stu Tomlinson
 Bill Tompkins
+Gal Topper
 Chris Toshok
 Ken Tossell
 Tom Tromey
--- a/ChangeLog.API	Sun Sep 16 17:52:49 2007 +0000
+++ b/ChangeLog.API	Sun Sep 16 17:58:40 2007 +0000
@@ -6,6 +6,9 @@
 		* PURPLE_MESSAGE_INVISIBLE flag, which can be used by
 		  purple_conv_im_send_with_flags to send a message, but not display it
 		  in the conversation
+		* serv_send_attention(), serv_got_attention(), as well as send_attention 
+		  and attention_types in PurplePluginProtocolInfo. This new API is used
+		  for zapping in MySpaceIM, buzzing in Yahoo, and nudging in MSN.
 		Changed:
 		* purple_prefs_load is now called within purple_prefs_init.
 		  The UI no longer needs to call it.
--- a/libpurple/certificate.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/certificate.c	Sun Sep 16 17:58:40 2007 +0000
@@ -1083,11 +1083,14 @@
 x509_tls_cached_show_cert(x509_tls_cached_ua_ctx *c, gint id)
 {
 	PurpleCertificate *disp_crt = c->vrq->cert_chain->data;
-	purple_certificate_display_x509(disp_crt);
 
 	/* Since clicking a button closes the request, show it again */
 	x509_tls_cached_user_auth(c->vrq, c->reason);
 
+	/* Show the certificate AFTER re-opening the dialog so that this
+	   appears above the other */
+	purple_certificate_display_x509(disp_crt);
+
 	x509_tls_cached_ua_ctx_free(c);
 }
 
--- a/libpurple/internal.h	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/internal.h	Sun Sep 16 17:58:40 2007 +0000
@@ -29,6 +29,10 @@
 # include <config.h>
 #endif
 
+/* for SIOCGIFCONF  in SKYOS */
+#ifdef SKYOS
+#include <net/sockios.h>
+#endif
 /*
  * If we're using NLS, make sure gettext works.  If not, then define
  * dummy macros in place of the normal gettext macros.
--- a/libpurple/plugins/tcl/tcl_cmds.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/plugins/tcl/tcl_cmds.c	Sun Sep 16 17:58:40 2007 +0000
@@ -544,12 +544,16 @@
 
 int tcl_cmd_cmd(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
 {
-	const char *cmds[] = { "register", "unregister", NULL };
-	enum { CMD_CMD_REGISTER, CMD_CMD_UNREGISTER } cmd;
+	const char *cmds[] = { "do", "help", "list", "register", "unregister", NULL };
+	enum { CMD_CMD_DO, CMD_CMD_HELP, CMD_CMD_LIST, CMD_CMD_REGISTER, CMD_CMD_UNREGISTER } cmd;
 	struct tcl_cmd_handler *handler;
-	Tcl_Obj *result = Tcl_GetObjResult(interp);
+	Tcl_Obj *list, *elem, *result = Tcl_GetObjResult(interp);
+	PurpleConversation *convo;
 	PurpleCmdId id;
+	PurpleCmdStatus status;
 	int error;
+	GList *l, *cur;
+	gchar *escaped, *errstr = NULL;
 
 	if (objc < 2) {
 		Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?");
@@ -560,6 +564,57 @@
 		return error;
 
 	switch (cmd) {
+	case CMD_CMD_DO:
+		if (objc != 4) {
+			Tcl_WrongNumArgs(interp, 2, objv, "conversation command");
+			return TCL_ERROR;
+		}
+		if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL)
+			return TCL_ERROR;
+		escaped = g_markup_escape_text(Tcl_GetString(objv[3]), -1);
+		status = purple_cmd_do_command(convo, Tcl_GetString(objv[3]),
+				escaped, &errstr);
+		g_free(escaped);
+		Tcl_SetStringObj(result, errstr ? (char *)errstr : "", -1);
+		g_free(errstr);
+		if (status != PURPLE_CMD_STATUS_OK) {
+			return TCL_ERROR;
+		}
+		break;
+	case CMD_CMD_HELP:
+		if (objc != 4) {
+			Tcl_WrongNumArgs(interp, 2, objv, "conversation name");
+			return TCL_ERROR;
+		}
+		if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL)
+			return TCL_ERROR;
+		l = cur = purple_cmd_help(convo, Tcl_GetString(objv[3]));
+		list = Tcl_NewListObj(0, NULL);
+		while (cur != NULL) {
+			elem = Tcl_NewStringObj((char *)cur->data, -1);
+			Tcl_ListObjAppendElement(interp, list, elem);
+			cur = g_list_next(cur);
+		}
+		g_list_free(l);
+		Tcl_SetObjResult(interp, list);
+		break;
+	case CMD_CMD_LIST:
+		if (objc != 3) {
+			Tcl_WrongNumArgs(interp, 2, objv, "conversation");
+			return TCL_ERROR;
+		}
+		if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL)
+			return TCL_ERROR;
+		l = cur = purple_cmd_list(convo);
+		list = Tcl_NewListObj(0, NULL);
+		while (cur != NULL) {
+			elem = Tcl_NewStringObj((char *)cur->data, -1);
+			Tcl_ListObjAppendElement(interp, list, elem);
+			cur = g_list_next(cur);
+		}
+		g_list_free(l);
+		Tcl_SetObjResult(interp, list);
+		break;
 	case CMD_CMD_REGISTER:
 		if (objc != 9) {
 			Tcl_WrongNumArgs(interp, 2, objv, "cmd arglist priority flags prpl_id proc helpstr");
--- a/libpurple/protocols/msn/slp.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Sun Sep 16 17:58:40 2007 +0000
@@ -345,7 +345,7 @@
 		if (xfer)
 		{
 			bin = (char *)purple_base64_decode(context, &bin_len);
-			file_size = GUINT32_FROM_LE(*((gsize *)bin + 2));
+			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 2));
 
 			uni_name = (gunichar2 *)(bin + 20);
 			while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
--- a/libpurple/protocols/myspace/CHANGES	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/protocols/myspace/CHANGES	Sun Sep 16 17:58:40 2007 +0000
@@ -1,3 +1,13 @@
+2007-08-28 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.17
+* Get server-side contact list from server on sign-on (partly implements
+  server-side contacts, ticket #2658).
+* Set local alias to username on sign-on, if not already set. This fixes
+  #2793, though this may not be the best way, other fixes under consideration.
+* Support myim:sendIM and addContact URLs with cID and uID parameters.
+* Fix #2722, only check for mail if "New mail notifications" is enabled.
+* Modularize msimprpl.
+
+
 2007-08-23 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.16
 * Add option to add all friends from myspace.com to your buddy list (#2660)
 * If a user doesn't have a picture, don't display an icon (instead of
@@ -6,6 +16,7 @@
 * Fix #2752, which led to duplicate groups
 * Fix #2720, crash/disconnect when adding a buddy that doesn't exist
   (You'll now receive an error when looking up invalid usernames).
+* Source-code release only.
 
 2007-08-22 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.15
 * Incomplete implementation of adding friends from myspace.com.
--- a/libpurple/protocols/myspace/myspace.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sun Sep 16 17:58:40 2007 +0000
@@ -683,6 +683,7 @@
 msim_incoming_im(MsimSession *session, MsimMessage *msg)
 {
 	gchar *username, *msg_msim_markup, *msg_purple_markup;
+	time_t time_received;
 
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
@@ -696,8 +697,12 @@
 	msg_purple_markup = msim_markup_to_html(session, msg_msim_markup);
 	g_free(msg_msim_markup);
 
-	serv_got_im(session->gc, username, msg_purple_markup, 
-			PURPLE_MESSAGE_RECV, time(NULL));
+	time_received = msim_msg_get_integer(msg, "date");
+	if (!time_received) {
+		time_received = time(NULL);
+	}
+
+	serv_got_im(session->gc, username, msg_purple_markup, PURPLE_MESSAGE_RECV, time_received);
 
 	g_free(username);
 	g_free(msg_purple_markup);
--- a/pidgin/gtkblist.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/pidgin/gtkblist.c	Sun Sep 16 17:58:40 2007 +0000
@@ -2622,6 +2622,7 @@
 	struct _pidgin_blist_node *gtknode;
 	GdkRectangle mon_size;
 	int sig;
+	const char *name;
 	
 	if (node == NULL)
 		return;
@@ -2679,7 +2680,9 @@
 
 	gtknode = node->ui_data;
 
+	name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget)));
 	gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE);
+	gtk_window_set_title(GTK_WINDOW(gtkblist->tipwindow), name ? name : _("Buddy List"));
 	gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE);
 	gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips");
 	g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event",
@@ -5206,6 +5209,10 @@
 		GdkPixbuf *emblem;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
+		const char *name = purple_chat_get_name(chat);
+		PurpleConversation *conv =
+				purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account);
+		gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv));
 
 		if(!insert_node(list, node, &iter))
 			return;
@@ -5221,15 +5228,20 @@
 			avatar = NULL;
 
 		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);
+		if (hidden) {
+			char *bold = g_strdup_printf("<b>%s</b>", mark);
+			g_free(mark);
+			mark = bold;
+		}
 
 		gtk_tree_store_set(gtkblist->treemodel, &iter,
 				STATUS_ICON_COLUMN, status,
 				STATUS_ICON_VISIBLE_COLUMN, TRUE,
 				BUDDY_ICON_COLUMN, avatar ? avatar : gtkblist->empty_avatar,
 				BUDDY_ICON_VISIBLE_COLUMN,  purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"),
-			        EMBLEM_COLUMN, emblem,
+				EMBLEM_COLUMN, emblem,
 				EMBLEM_VISIBLE_COLUMN, emblem != NULL,
-		 	        PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
+				PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
 				PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 				NAME_COLUMN, mark,
 				GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
--- a/pidgin/gtkconv.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Sep 16 17:58:40 2007 +0000
@@ -139,6 +139,7 @@
 static void generate_send_to_items(PidginWindow *win);
 
 /* Prototypes. <-- because Paco-Paco hates this comment. */
+static gboolean infopane_entry_activate(PidginConversation *gtkconv);
 static void got_typing_keypress(PidginConversation *gtkconv, gboolean first);
 static void gray_stuff_out(PidginConversation *gtkconv);
 static GList *generate_invite_user_names(PurpleConnection *gc);
@@ -158,6 +159,8 @@
 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
 static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
 static gboolean alias_double_click_cb(GtkWidget *widget, GdkEventButton *event, PidginConversation *gtkconv);
+static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv);
+static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv);
 
 static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y,
 		int width, int height);
@@ -1791,17 +1794,108 @@
 }
 
 static gboolean
+conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
+{
+	PidginWindow *win;
+	PurpleConversation *conv;
+	int curconv;
+
+	conv     = gtkconv->active_conv;
+	win      = gtkconv->win;
+	curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
+
+	/* If CTRL was held down... */
+	if (event->state & GDK_CONTROL_MASK) {
+		switch (event->keyval) {
+			case GDK_Page_Down:
+			case ']':
+				if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv + 1))
+					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
+				else
+					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv + 1);
+				return TRUE;
+				break;
+
+			case GDK_Page_Up:
+			case '[':
+				if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv - 1))
+					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), -1);
+				else
+					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv - 1);
+				return TRUE;
+				break;
+
+			case GDK_Tab:
+			case GDK_ISO_Left_Tab:
+				if (event->state & GDK_SHIFT_MASK) {
+					move_to_next_unread_tab(gtkconv, FALSE);
+				} else {
+					move_to_next_unread_tab(gtkconv, TRUE);
+				}
+
+				return TRUE;
+				break;
+
+			case GDK_comma:
+				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
+						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
+						curconv - 1);
+				break;
+
+			case GDK_period:
+				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
+						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
+#if GTK_CHECK_VERSION(2,2,0)
+						(curconv + 1) % gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook)));
+#else
+						(curconv + 1) % g_list_length(GTK_NOTEBOOK(win->notebook)->children));
+#endif
+				break;
+
+		} /* End of switch */
+	}
+
+	/* If ALT (or whatever) was held down... */
+	else if (event->state & GDK_MOD1_MASK)
+	{
+		if (event->keyval > '0' && event->keyval <= '9')
+		{
+			guint switchto = event->keyval - '1';
+			if (switchto < pidgin_conv_window_get_gtkconv_count(win))
+				gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), switchto);
+
+			return TRUE;
+		}
+	}
+
+	/* If neither CTRL nor ALT were held down... */
+	else
+	{
+		switch (event->keyval) {
+		case GDK_F2:
+			if (gtk_widget_is_focus(GTK_WIDGET(win->notebook))) {
+				infopane_entry_activate(gtkconv);
+				return TRUE;
+			}
+			break;
+		}
+	}
+	return FALSE;
+}
+
+static gboolean
 entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
 {
 	PidginWindow *win;
 	PurpleConversation *conv;
 	PidginConversation *gtkconv;
-	int curconv;
 
 	gtkconv  = (PidginConversation *)data;
 	conv     = gtkconv->active_conv;
 	win      = gtkconv->win;
-	curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
+	
+	if (conv_keypress_common(gtkconv, event))
+		return TRUE;
 
 	/* If CTRL was held down... */
 	if (event->state & GDK_CONTROL_MASK) {
@@ -1899,88 +1993,32 @@
 
 				return TRUE;
 				break;
-
-			case GDK_Page_Down:
-			case ']':
-				if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv + 1))
-					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
-				else
-					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv + 1);
-				return TRUE;
-				break;
-
-			case GDK_Page_Up:
-			case '[':
-				if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv - 1))
-					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), -1);
-				else
-					gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv - 1);
-				return TRUE;
-				break;
-
-			case GDK_Tab:
-			case GDK_ISO_Left_Tab:
-				if (event->state & GDK_SHIFT_MASK) {
-					move_to_next_unread_tab(gtkconv, FALSE);
-				} else {
-					move_to_next_unread_tab(gtkconv, TRUE);
-				}
-
-				return TRUE;
-				break;
-
-			case GDK_comma:
-				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
-						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
-						curconv - 1);
-				break;
-
-			case GDK_period:
-				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
-						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
-#if GTK_CHECK_VERSION(2,2,0)
-						(curconv + 1) % gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook)));
-#else
-						(curconv + 1) % g_list_length(GTK_NOTEBOOK(win->notebook)->children));
-#endif
-				break;
-
 		} /* End of switch */
 	}
 
 	/* If ALT (or whatever) was held down... */
-	else if (event->state & GDK_MOD1_MASK)
-	{
-		if (event->keyval > '0' && event->keyval <= '9')
-		{
-			guint switchto = event->keyval - '1';
-			if (switchto < pidgin_conv_window_get_gtkconv_count(win))
-				gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), switchto);
-
-			return TRUE;
-		}
+	else if (event->state & GDK_MOD1_MASK) 	{
+
 	}
 
 	/* If neither CTRL nor ALT were held down... */
-	else
-	{
-		switch (event->keyval)
-		{
-			case GDK_Tab:
-				if (gtkconv->entry != entry)
-					break;
-				return tab_complete(conv);
+	else {
+		switch (event->keyval) {
+		case GDK_Tab:
+			if (gtkconv->entry != entry)
 				break;
-
-			case GDK_Page_Up:
-				gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
-				return TRUE;
-				break;
-
-			case GDK_Page_Down:
-				gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
-				return TRUE;
-				break;
+			return tab_complete(conv);
+			break;
+
+		case GDK_Page_Up:
+			gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
+			return TRUE;
+			break;
+
+		case GDK_Page_Down:
+			gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
+			return TRUE;
+			break;
 
 		}
 	}
@@ -2021,6 +2059,7 @@
 
 	/* If we have a valid key for the conversation display, then exit */
 	if ((event->state & GDK_CONTROL_MASK) ||
+		(event->keyval == GDK_F6) ||
 		(event->keyval == GDK_F10) ||
 		(event->keyval == GDK_Shift_L) ||
 		(event->keyval == GDK_Shift_R) ||
@@ -2031,11 +2070,17 @@
 		(event->keyval == GDK_Down) ||
 		(event->keyval == GDK_Left) ||
 		(event->keyval == GDK_Right) ||
+		(event->keyval == GDK_Page_Up) ||
+		(event->keyval == GDK_Page_Down) ||
 		(event->keyval == GDK_Home) ||
 		(event->keyval == GDK_End) ||
 		(event->keyval == GDK_Tab) ||
 		(event->keyval == GDK_ISO_Left_Tab))
-			return FALSE;
+	{
+		if (event->type == GDK_KEY_PRESS)
+			return conv_keypress_common(gtkconv, event);
+		return FALSE;
+	}
 
 	if (event->type == GDK_KEY_RELEASE)
 		gtk_widget_grab_focus(gtkconv->entry);
@@ -4393,6 +4438,10 @@
 
 	g_signal_connect(G_OBJECT(list), "button_press_event",
 					 G_CALLBACK(right_click_chat_cb), gtkconv);
+	g_signal_connect(G_OBJECT(list), "motion-notify-event",
+					 G_CALLBACK(pidgin_userlist_motion_cb), gtkconv);
+	g_signal_connect(G_OBJECT(list), "leave-notify-event",
+					 G_CALLBACK(pidgin_userlist_motion_cb), gtkconv);
 	g_signal_connect(G_OBJECT(list), "popup-menu",
 			 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv);
 	g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv);
@@ -4437,6 +4486,8 @@
 	int timeout;
 	PidginConversation *gtkconv;   /* This is the Pidgin conversation that
 	                                  triggered the tooltip */
+	int userlistx;
+	int userlisty;
 } tooltip;
 
 static void
@@ -4493,6 +4544,72 @@
 	return FALSE;
 }
 
+static gboolean
+pidgin_userlist_tooltip_timeout(PidginConversation *gtkconv)
+{
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleConversation *conv = gtkconv->active_conv;
+	PidginChatPane *gtkchat;
+	PurpleConnection *gc;
+	PurpleBlistNode *node = NULL;
+	PurpleAccount *account;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	GtkTreeViewColumn *column;
+	gchar *who;
+	int x, y;
+
+	gtkchat = gtkconv->u.chat;
+	account = purple_conversation_get_account(conv);
+	gc = account->gc;
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
+
+	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list),
+								  tooltip.userlistx, tooltip.userlisty, &path, &column, &x, &y);
+
+	if (path == NULL)
+		return FALSE;
+
+	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);
+
+	node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who));
+	if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) 
+		pidgin_blist_draw_tooltip(node, gtkconv->infopane);
+
+	g_free(who);
+	gtk_tree_path_free(path);
+
+
+	return FALSE;
+}
+
+static gboolean 
+pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv)
+{
+	PurpleConversation *conv;
+	int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
+	
+	pidgin_blist_tooltip_destroy();
+	if (delay == 0)
+		return FALSE;
+
+	if (tooltip.timeout != 0) 
+		g_source_remove(tooltip.timeout);
+	
+	conv = gtkconv->active_conv;
+
+	tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_userlist_tooltip_timeout, gtkconv);
+	tooltip.gtkconv = gtkconv;
+	tooltip.userlistx = event->x;
+	tooltip.userlisty = event->y;
+
+	return FALSE;
+}
+ 
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
@@ -5625,6 +5742,7 @@
 		account, name, displaying, conv, flags);
 	g_free(displaying);
 }
+
 static void
 pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
 {
@@ -7238,10 +7356,10 @@
 				"conversation-displayed", gtkconv);
 	}
 
-	/* XXX: If this is a chat:
-	 * 	- populate the userlist
-	 * 	- set the topic
-	 */
+	if (conv->type == PURPLE_CONV_TYPE_CHAT) {
+		pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
+		pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
+	}
 
 	return TRUE;
 }
@@ -7863,7 +7981,7 @@
 infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
 {
 	if (e->type == GDK_2BUTTON_PRESS && e->button == 1) {
-		if (alias_double_click_cb(widget, e, gtkconv))
+		if (infopane_entry_activate(gtkconv))
 			return TRUE;
 	}
 
@@ -8320,16 +8438,12 @@
 }
 
 static gboolean
-alias_double_click_cb(GtkWidget *widget, GdkEventButton *event, PidginConversation *gtkconv)
+infopane_entry_activate(PidginConversation *gtkconv)
 {
 	GtkWidget *entry = NULL;
         PurpleConversation *conv = gtkconv->active_conv;
 	const char *text = NULL;
 
-	if (event->button != 1 || event->type != GDK_2BUTTON_PRESS) {
-		return FALSE;
-	}
-
 	if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
 		/* There's already an entry for alias. Let's not create another one. */
 		return FALSE;
@@ -8372,9 +8486,30 @@
 	gtk_widget_hide(gtkconv->infopane);
 	gtk_widget_grab_focus(entry);
 
+	return TRUE;
+}
+
+static gboolean
+alias_double_click_cb(GtkWidget *widget, GdkEventButton *event, PidginConversation *gtkconv)
+{
+	/* I'm keeping this around in case we decide to handle double-clicking tabs
+	 *  (or some other label) this way. */
+	if (event->button != 1 || event->type != GDK_2BUTTON_PRESS) {
+		return FALSE;
+	}
+	
+	infopane_entry_activate(gtkconv);
 	return FALSE;
 }
 
+static gboolean
+window_keypress_cb(GtkWidget *widget, GdkEventKey *event, PidginWindow *win)
+{
+	PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+
+	return conv_keypress_common(gtkconv, event);
+}
+
 static void
 switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
                gpointer user_data)
@@ -8570,6 +8705,11 @@
 	g_signal_connect(G_OBJECT(win->window), "focus_in_event",
 	                 G_CALLBACK(focus_win_cb), win);
 
+	/* Intercept keystrokes from the menu items */
+	g_signal_connect(G_OBJECT(win->window), "key_press_event",
+					 G_CALLBACK(window_keypress_cb), win);
+	
+
 	/* Create the notebook. */
 	win->notebook = gtk_notebook_new();
 
--- a/pidgin/gtkimhtml.c	Sun Sep 16 17:52:49 2007 +0000
+++ b/pidgin/gtkimhtml.c	Sun Sep 16 17:58:40 2007 +0000
@@ -4890,7 +4890,7 @@
 		} else if (c == '"') {
 			str = g_string_append(str, "&quot;");
 		} else if (c == '\n') {
-			str = g_string_append(str, "<br>");
+			str = g_string_append(str, "<br>\n");
 		} else {
 			str = g_string_append_unichar(str, c);
 		}