changeset 26746:7c2506045671

merge of '9804728d8412930412ec4faaa29230268879586d' and '99afc19a87766c3beb24ccc126de72da4fef3be9'
author Etan Reisner <pidgin@unreliablesource.net>
date Fri, 20 Mar 2009 00:34:17 +0000
parents 65d973e8de78 (current diff) c884b702ec17 (diff)
children 60b39347b59c
files
diffstat 15 files changed, 540 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Mar 11 21:49:08 2009 +0000
+++ b/ChangeLog	Fri Mar 20 00:34:17 2009 +0000
@@ -6,8 +6,9 @@
 	  project.  With some minor additions and clean ups from Paul Aurich.
 
 	XMPP:
-	* Add support for in-band bytestreams (XEP-0047).
-	* Add support for attention (XEP-0224).
+	* Add support for in-band bytestreams for file transfers (XEP-0047).
+	* Add support for sending attentions (equivalent to "buzz" and "nudge")
+	  using the command /buzz (XEP-0224).
 
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
@@ -18,6 +19,9 @@
 	* Pressing the Enter key in the message entry box of the New Status
 	  dialog and various other dialogs now causes the cursor to move to
 	  the next line.
+	* Created a unified Buddy Pounce notification window for all pounces
+	  where "Pop up a notification" is selected, which avoids having a
+	  new dialog box every time a pounce is triggered. (Jorge VillaseƱor) 
 
 version 2.5.5 (03/01/2009):
 	libpurple:
--- a/ChangeLog.API	Wed Mar 11 21:49:08 2009 +0000
+++ b/ChangeLog.API	Fri Mar 20 00:34:17 2009 +0000
@@ -26,6 +26,14 @@
 		* purple_request_field_set_ui_data
 		* purple_strequal
 		* xmlnode_from_file
+		* xmlnode_set_attrib_full
+
+		Changed:
+		* xmlnode_remove_attrib now removes all attributes with the
+		same name.  Previously, it would remove the first one found,
+		which was completely non-deterministic.  If you want to remove
+		the attribute with no namespace, then use NULL with
+		xmlnode_remove_with_namespace.
 
 		Deprecated:
 		* purple_buddy_get_local_alias
@@ -40,6 +48,8 @@
 		* purple_status_set_attr_string
 		* purple_presence_add_status
 		* purple_presence_add_list
+		* xmlnode_set_attrib_with_namespace
+		* xmlnode_set_attrib_with_prefix
 
 	pidgin:
 		Added:
@@ -52,6 +62,7 @@
 		* pidgin_blist_get_theme
 		* pidgin_sound_is_customized
 		* pidgin_utils_init, pidgin_utils_uninit
+		* pidgin_notify_pounce_add
 
 	perl:
 		Changed:
--- a/libpurple/account.h	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/account.h	Fri Mar 20 00:34:17 2009 +0000
@@ -42,6 +42,7 @@
 
 #include "connection.h"
 #include "log.h"
+#include "privacy.h"
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
@@ -141,7 +142,7 @@
 	 */
 	GSList *permit;             /**< Permit list.                           */
 	GSList *deny;               /**< Deny list.                             */
-	int perm_deny;              /**< The permit/deny setting.               */
+	PurplePrivacyType perm_deny;  /**< The permit/deny setting.               */
 
 	GList *status_types;        /**< Status types.                          */
 
--- a/libpurple/blist.h	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/blist.h	Fri Mar 20 00:34:17 2009 +0000
@@ -595,7 +595,7 @@
  * @param contact The optional contact to place the buddy in.
  * @param group   The group to add the new buddy to.
  * @param node    The insertion point.  Pass in NULL to add the node as
- *                the last child in the given group.
+ *                the first child in the given group.
  */
 void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
 
--- a/libpurple/dnssrv.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/dnssrv.c	Fri Mar 20 00:34:17 2009 +0000
@@ -175,9 +175,11 @@
 
 end:
 	size = g_list_length(ret);
+	/* TODO: Check return value */
 	write(out, &size, sizeof(int));
 	while (ret != NULL)
 	{
+		/* TODO: Check return value */
 		write(out, ret->data, sizeof(PurpleSrvResponse));
 		g_free(ret->data);
 		ret = g_list_remove(ret, ret->data);
--- a/libpurple/protocols/bonjour/parser.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Fri Mar 20 00:34:17 2009 +0000
@@ -91,14 +91,12 @@
 		xmlnode_set_namespace(node, (const char*) namespace);
 
 		for(i=0; i < nb_attributes * 5; i+=5) {
+			const char *name = (const char *)attributes[i];
+			const char *prefix = (const char *)attributes[i+1];
+			const char *attrib_ns = (const char *)attributes[i+2];
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_malloc(attrib_len + 1);
-			char *attrib_ns = NULL;
-
-			if (attributes[i+2]) {
-				attrib_ns = g_strdup((char*)attributes[i+2]);
-			}
 
 			memcpy(attrib, attributes[i+3], attrib_len);
 			attrib[attrib_len] = '\0';
@@ -106,9 +104,8 @@
 			txt = attrib;
 			attrib = purple_unescape_html(txt);
 			g_free(txt);
-			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
+			xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
 			g_free(attrib);
-			g_free(attrib_ns);
 		}
 
 		bconv->current = node;
--- a/libpurple/protocols/jabber/parser.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/protocols/jabber/parser.c	Fri Mar 20 00:34:17 2009 +0000
@@ -86,6 +86,8 @@
 			}
 		}
 		for(i=0; i < nb_attributes * 5; i+=5) {
+			const char *name = (const char *)attributes[i];
+			const char *prefix = (const char *)attributes[i+1];
 			const char *attrib_ns = (const char *)attributes[i+2];
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
@@ -97,7 +99,7 @@
 			txt = attrib;
 			attrib = purple_unescape_html(txt);
 			g_free(txt);
-			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
+			xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
 			g_free(attrib);
 		}
 
--- a/libpurple/protocols/jabber/si.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Fri Mar 20 00:34:17 2009 +0000
@@ -1096,6 +1096,7 @@
 			"jabber_si_xfer_ibb_send_data: error reading from file\n");
 		purple_xfer_cancel_local(xfer);
 	}
+	g_free(data);
 }
 
 static void
--- a/libpurple/protocols/oscar/oscar.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Mar 20 00:34:17 2009 +0000
@@ -4789,7 +4789,7 @@
 
 		status_html = purple_status_get_attr_string(status, "message");
 
-		if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
+		if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
 		{
 			/* This is needed for us to un-set any previous away message. */
 			away = g_strdup("");
--- a/libpurple/util.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/util.c	Fri Mar 20 00:34:17 2009 +0000
@@ -2850,6 +2850,12 @@
 	return "icon";
 }
 
+/*
+ * TODO: Consider using something faster than SHA-1, such as MD5, MD4
+ *       or CRC32.  Are there security implications to that?  Would
+ *       probably be a good idea to benchmark some algorithms with
+ *       3KB-10KB chunks of data (typical buddy icon sizes).
+ */
 char *
 purple_util_get_image_checksum(gconstpointer image_data, size_t image_len)
 {
@@ -4038,6 +4044,13 @@
 				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
 
 	if (purple_strcasestr(url, "https://") != NULL) {
+		if (!purple_ssl_is_supported()) {
+			purple_util_fetch_url_error(gfud,
+					_("Unable to connect to %s: Server requires TLS/SSL, but no TLS/SSL support was found."),
+					gfud->website.address);
+			return NULL;
+		}
+
 		gfud->is_ssl = TRUE;
 		gfud->ssl_connection = purple_ssl_connect(NULL,
 				gfud->website.address, gfud->website.port,
--- a/libpurple/xmlnode.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/xmlnode.c	Fri Mar 20 00:34:17 2009 +0000
@@ -27,6 +27,7 @@
  * libxode uses memory pools that we simply have no need for, I decided to
  * write my own stuff.  Also, re-writing this lets me be as lightweight
  * as I want to be.  Thank you libxode for giving me a good starting point */
+#define _PURPLE_XMLNODE_C_
 
 #include "debug.h"
 #include "internal.h"
@@ -126,21 +127,28 @@
 	g_return_if_fail(node != NULL);
 	g_return_if_fail(attr != NULL);
 
-	for(attr_node = node->child; attr_node; attr_node = attr_node->next)
-	{
+	attr_node = node->child;
+	while (attr_node) {
 		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
 				purple_strequal(attr_node->name, attr))
 		{
-			if(sibling == NULL) {
-				node->child = attr_node->next;
-			} else {
-				sibling->next = attr_node->next;
-			}
 			if (node->lastchild == attr_node) {
 				node->lastchild = sibling;
 			}
-			xmlnode_free(attr_node);
-			return;
+			if (sibling == NULL) {
+				node->child = attr_node->next;
+				xmlnode_free(attr_node);
+				attr_node = node->child;
+			} else {
+				sibling->next = attr_node->next;
+				sibling = attr_node->next;
+				xmlnode_free(attr_node);
+				attr_node = sibling;
+			}
+		}
+		else
+		{
+			attr_node = attr_node->next;
 		}
 		sibling = attr_node;
 	}
@@ -178,24 +186,25 @@
 void
 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value)
 {
-	xmlnode *attrib_node;
-
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(attr != NULL);
-	g_return_if_fail(value != NULL);
-
 	xmlnode_remove_attrib(node, attr);
-
-	attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB);
-
-	attrib_node->data = g_strdup(value);
-
-	xmlnode_insert_child(node, attrib_node);
+	xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
 }
 
 void
 xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value)
 {
+	xmlnode_set_attrib_full(node, attr, xmlns, NULL, value);
+}
+
+void
+xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value)
+{
+	xmlnode_set_attrib_full(node, attr, NULL, prefix, value);
+}
+
+void
+xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
+{
 	xmlnode *attrib_node;
 
 	g_return_if_fail(node != NULL);
@@ -207,22 +216,6 @@
 
 	attrib_node->data = g_strdup(value);
 	attrib_node->xmlns = g_strdup(xmlns);
-
-	xmlnode_insert_child(node, attrib_node);
-}
-
-void
-xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value)
-{
-	xmlnode *attrib_node;
-
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(attr != NULL);
-	g_return_if_fail(value != NULL);
-
-	attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB);
-
-	attrib_node->data = g_strdup(value);
 	attrib_node->prefix = g_strdup(prefix);
 
 	xmlnode_insert_child(node, attrib_node);
@@ -585,7 +578,8 @@
 		}
 
 		for(i=0; i < nb_attributes * 5; i+=5) {
-			const char *prefix = (const char *)attributes[i + 1];
+			const char *name = (const char *)attributes[i];
+			const char *prefix = (const char *)attributes[i+1];
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_malloc(attrib_len + 1);
@@ -594,11 +588,7 @@
 			txt = attrib;
 			attrib = purple_unescape_html(txt);
 			g_free(txt);
-			if (prefix && *prefix) {
-				xmlnode_set_attrib_with_prefix(node, (const char*) attributes[i], prefix, attrib);
-			} else {
-				xmlnode_set_attrib(node, (const char*) attributes[i], attrib);
-			}
+			xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
 			g_free(attrib);
 		}
 
--- a/libpurple/xmlnode.h	Wed Mar 11 21:49:08 2009 +0000
+++ b/libpurple/xmlnode.h	Fri Mar 20 00:34:17 2009 +0000
@@ -157,6 +157,7 @@
  */
 void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_XMLNODE_C_)
 /**
  * Sets a prefixed attribute for a node
  *
@@ -164,6 +165,8 @@
  * @param attr   The name of the attribute to set
  * @param prefix The prefix of the attribute to ste
  * @param value  The value of the attribute
+ *
+ * @deprecated Use xmlnode_set_attrib_full instead.
  */
 void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value);
 
@@ -174,8 +177,25 @@
  * @param attr  The name of the attribute to set
  * @param xmlns The namespace of the attribute to ste
  * @param value The value of the attribute
+ *
+ * @deprecated Use xmlnode_set_attrib_full instead.
  */
 void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value);
+#endif /* PURPLE_DISABLE_DEPRECATED */
+
+/**
+ * Sets a namespaced attribute for a node
+ *
+ * @param node   The node to set an attribute for.
+ * @param attr   The name of the attribute to set
+ * @param xmlns  The namespace of the attribute to ste
+ * @param prefix The prefix of the attribute to ste
+ * @param value  The value of the attribute
+ *
+ * @since 2.6.0
+ */
+void xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns,
+	const char *prefix, const char *value);
 
 /**
  * Gets an attribute from a node.
--- a/pidgin/gtknotify.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/pidgin/gtknotify.c	Fri Mar 20 00:34:17 2009 +0000
@@ -28,6 +28,7 @@
 
 #include <gdk/gdkkeysyms.h>
 
+#include "account.h"
 #include "connection.h"
 #include "debug.h"
 #include "prefs.h"
@@ -37,6 +38,7 @@
 #include "gtkblist.h"
 #include "gtkimhtml.h"
 #include "gtknotify.h"
+#include "gtkpounce.h"
 #include "gtkutils.h"
 
 typedef struct
@@ -57,6 +59,13 @@
 typedef struct
 {
 	PurpleAccount *account;
+	PurplePounce *pounce;
+} PidginNotifyPounceData;
+
+
+typedef struct
+{
+	PurpleAccount *account;
 	GtkListStore *model;
 	GtkWidget *treeview;
 	GtkWidget *window;
@@ -80,21 +89,44 @@
 	COLUMNS_PIDGIN_MAIL
 };
 
-typedef struct _PidginMailDialog PidginMailDialog;
+enum
+{
+	PIDGIN_POUNCE_ICON,
+	PIDGIN_POUNCE_ALIAS,
+	PIDGIN_POUNCE_EVENT,
+	PIDGIN_POUNCE_TEXT,
+	PIDGIN_POUNCE_DATE,
+	PIDGIN_POUNCE_DATA,
+	PIDGIN_POUNCE_COLUMNS
+};
 
-struct _PidginMailDialog
+typedef struct _PidginNotifyDialog PidginNotifyDialog;
+typedef PidginNotifyDialog PidginMailDialog;
+
+struct _PidginNotifyDialog
 {
 	GtkWidget *dialog;
 	GtkWidget *treeview;
 	GtkTreeStore *treemodel;
 	GtkLabel *label;
 	GtkWidget *open_button;
+	GtkWidget *dismiss_button;
+	GtkWidget *edit_button;
 	int total_count;
 	gboolean in_use;
 };
 
-static PidginMailDialog *mail_dialog = NULL;
+typedef enum
+{
+	PIDGIN_NOTIFY_MAIL,
+	PIDGIN_NOTIFY_POUNCE,
+        PIDGIN_NOTIFY_TYPES
+} PidginNotifyType;
 
+static PidginNotifyDialog *mail_dialog = NULL;
+static PidginNotifyDialog *pounce_dialog = NULL;
+
+static GtkWidget *pidgin_get_notification_dialog(PidginNotifyType type);
 static void *pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
 									const char **subjects,
 									const char **froms, const char **tos,
@@ -109,6 +141,159 @@
 }
 
 static void
+pounce_response_close(PidginNotifyDialog *dialog)
+{
+	GtkTreeIter iter;
+	PidginNotifyPounceData *pounce_data;
+
+	while (gtk_tree_model_get_iter_first(
+				GTK_TREE_MODEL(pounce_dialog->treemodel), &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+				PIDGIN_POUNCE_DATA, &pounce_data,
+				-1);
+		gtk_tree_store_remove(dialog->treemodel, &iter);
+
+		g_free(pounce_data);
+	}
+
+	gtk_widget_destroy(pounce_dialog->dialog);
+	g_free(pounce_dialog);
+	pounce_dialog = NULL;
+}
+
+static void
+delete_foreach(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	PidginNotifyPounceData *pounce_data;
+
+	gtk_tree_model_get(model, iter,
+			PIDGIN_POUNCE_DATA, &pounce_data,
+			-1);
+
+	if (pounce_data != NULL)
+		g_free(pounce_data);
+}
+
+static void
+append_to_list(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	GList **list = data;
+	*list = g_list_prepend(*list, gtk_tree_path_copy(path));
+}
+static void
+pounce_response_dismiss()
+{
+	GtkTreeSelection *selection;
+	GList *list = NULL;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview));
+	gtk_tree_selection_selected_foreach(selection, delete_foreach, pounce_dialog);
+	gtk_tree_selection_selected_foreach(selection, append_to_list, &list);
+
+	while (list) {
+		GtkTreeIter iter;
+		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+					list->data)) {
+			gtk_tree_store_remove(GTK_TREE_STORE(pounce_dialog->treemodel), &iter);
+		}
+		gtk_tree_path_free(list->data);
+		list = g_list_delete_link(list, list);
+	}
+}
+
+static void
+pounce_response_edit_cb(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	PidginNotifyPounceData *pounce_data;
+	PidginNotifyDialog *dialog = (PidginNotifyDialog*)data;
+	PurplePounce *pounce;
+	GList *list;
+
+	list = purple_pounces_get_all();
+
+	gtk_tree_model_get(GTK_TREE_MODEL(dialog->treemodel), iter,
+			PIDGIN_POUNCE_DATA, &pounce_data,
+			-1);
+	
+	for (; list != NULL; list = list->next) {
+		pounce = list->data;
+		if (pounce == pounce_data->pounce) {
+			pidgin_pounce_editor_show(pounce_data->account, NULL, pounce_data->pounce);
+			return;
+		}
+	}
+
+	purple_debug_warning("gtknotify", "Pounce was destroyed.\n");
+}
+
+static void
+pounce_response_cb(GtkDialog *dlg, gint id, PidginNotifyDialog *dialog)
+{
+	GtkTreeSelection *selection = NULL;
+
+	switch (id) {
+		case GTK_RESPONSE_CLOSE:
+		case GTK_RESPONSE_DELETE_EVENT:
+			pounce_response_close(dialog);
+			break;
+		case GTK_RESPONSE_NO:
+			pounce_response_dismiss();
+			break;
+		case GTK_RESPONSE_APPLY:
+			selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
+			gtk_tree_selection_selected_foreach(selection, pounce_response_edit_cb,
+					dialog);
+			break;
+	}
+}
+
+static void
+pounce_row_selected_cb(GtkTreeView *tv, GtkTreePath *path,
+	GtkTreeViewColumn *col, gpointer data)
+{
+	GtkTreeIter iter;
+	GtkTreeSelection *selection;
+	gboolean selected;
+	GList *list;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview));
+
+	selected = gtk_tree_selection_get_selected(selection,
+			NULL, &iter);
+
+	if (selected) {
+		PurplePounce *pounce;
+		PidginNotifyPounceData *pounce_data;
+
+		list = purple_pounces_get_all();
+
+		gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+				PIDGIN_POUNCE_DATA, &pounce_data,
+				-1);
+
+		gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE);
+
+		for (; list != NULL; list = list->next) {
+			pounce = list->data;
+			if (pounce == pounce_data->pounce) {
+				gtk_widget_set_sensitive(pounce_dialog->edit_button, TRUE);
+				break;
+			}
+		}
+	
+		gtk_widget_set_sensitive(pounce_dialog->dismiss_button, TRUE);
+	} else {
+		gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE);
+		gtk_widget_set_sensitive(pounce_dialog->dismiss_button, FALSE);
+	}
+
+
+}
+
+static void
 email_response_cb(GtkDialog *dlg, gint id, PidginMailDialog *dialog)
 {
 	PidginNotifyMailData *data = NULL;
@@ -342,89 +527,7 @@
 static GtkWidget *
 pidgin_get_mail_dialog(void)
 {
-	if (mail_dialog == NULL) {
-		GtkWidget *dialog = NULL;
-		GtkWidget *label;
-		GtkWidget *sw;
-		GtkCellRenderer *rend;
-		GtkTreeViewColumn *column;
-		GtkWidget *button = NULL;
-		GtkWidget *vbox = NULL;
-
-		dialog = gtk_dialog_new_with_buttons(_("New Mail"), NULL, 0,
-						     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-						     NULL);
-		gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed");
-		g_signal_connect(G_OBJECT(dialog), "focus-in-event",
-					G_CALLBACK(mail_window_focus_cb), NULL);
-
-		gtk_dialog_add_button(GTK_DIALOG(dialog),
-					 _("Open All Messages"), GTK_RESPONSE_ACCEPT);
-
-		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
-						 PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES);
-
-		/* make "Open All Messages" the default response */
-		gtk_dialog_set_default_response(GTK_DIALOG(dialog),
-						GTK_RESPONSE_ACCEPT);
-
-		/* Setup the dialog */
-		gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
-		gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
-		gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
-		gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
-
-		/* Vertical box */
-		vbox = GTK_DIALOG(dialog)->vbox;
-
-		/* Golden ratio it up! */
-		gtk_widget_set_size_request(dialog, 550, 400);
-
-		sw = gtk_scrolled_window_new(NULL, NULL);
-		gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-
-		mail_dialog = g_new0(PidginMailDialog, 1);
-		mail_dialog->dialog = dialog;
-		mail_dialog->open_button = button;
-
-		mail_dialog->treemodel = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL,
-						GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
-		mail_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mail_dialog->treemodel));
-		g_object_unref(G_OBJECT(mail_dialog->treemodel));
-		gtk_tree_view_set_search_column(GTK_TREE_VIEW(mail_dialog->treeview), PIDGIN_MAIL_TEXT);
-		gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(mail_dialog->treeview),
-			             pidgin_tree_view_search_equal_func, NULL, NULL);
-
-		g_signal_connect(G_OBJECT(dialog), "response",
-						 G_CALLBACK(email_response_cb), mail_dialog);
-		g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview))),
-						 "changed", G_CALLBACK(selection_changed_cb), mail_dialog);
-		g_signal_connect(G_OBJECT(mail_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
-
-		gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mail_dialog->treeview), FALSE);
-		gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mail_dialog->treeview), TRUE);
-		gtk_container_add(GTK_CONTAINER(sw), mail_dialog->treeview);
-
-		column = gtk_tree_view_column_new();
-		gtk_tree_view_column_set_resizable(column, TRUE);
-		rend = gtk_cell_renderer_pixbuf_new();
-		gtk_tree_view_column_pack_start(column, rend, FALSE);
-		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL);
-		rend = gtk_cell_renderer_text_new();
-		gtk_tree_view_column_pack_start(column, rend, TRUE);
-		gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL);
-		gtk_tree_view_append_column(GTK_TREE_VIEW(mail_dialog->treeview), column);
-
-		label = gtk_label_new(NULL);
-		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
-		gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-		gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	}
-
-	return mail_dialog->dialog;
+	return pidgin_get_notification_dialog(PIDGIN_NOTIFY_MAIL);
 }
 
 /* count == 0 means this is a detailed mail notification.
@@ -1001,8 +1104,10 @@
 	{
 		PidginNotifyMailData *data = (PidginNotifyMailData *)ui_handle;
 
-		g_free(data->url);
-		g_free(data);
+		if (data) {
+			g_free(data->url);
+			g_free(data);
+		}
 	}
 	else if (type == PURPLE_NOTIFY_SEARCHRESULTS)
 	{
@@ -1234,6 +1339,228 @@
 	return NULL;
 }
 
+static GtkWidget *
+pidgin_get_dialog(PidginNotifyType type, GtkTreeStore *treemodel)
+{
+	GtkWidget *dialog = NULL;
+	GtkWidget *label = NULL;
+	GtkWidget *sw;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *column;
+	GtkWidget *button = NULL;
+	GtkWidget *vbox = NULL;
+	GtkTreeSelection *sel;
+	PidginNotifyDialog *spec_dialog = NULL;
+	
+	g_return_val_if_fail(type < PIDGIN_NOTIFY_TYPES, NULL);
+	
+	dialog = gtk_dialog_new_with_buttons(NULL, NULL, 0,
+			GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+			NULL);
+
+	/* Setup the dialog */
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
+	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+
+	/* Vertical box */
+	vbox = GTK_DIALOG(dialog)->vbox;
+
+	/* Golden ratio it up! */
+	gtk_widget_set_size_request(dialog, 550, 400);
+
+	sw = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+	spec_dialog = g_new0(PidginNotifyDialog, 1);
+	spec_dialog->dialog = dialog;
+	spec_dialog->open_button = button;
+
+	spec_dialog->treemodel = treemodel;
+	spec_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(spec_dialog->treemodel));
+	g_object_unref(G_OBJECT(spec_dialog->treemodel));
+	
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog->treeview), TRUE);
+	gtk_container_add(GTK_CONTAINER(sw), spec_dialog->treeview);
+
+	if (type == PIDGIN_NOTIFY_MAIL) {
+		gtk_window_set_title(GTK_WINDOW(dialog), _("New Mail"));
+		gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed");
+		g_signal_connect(G_OBJECT(dialog), "focus-in-event",
+					G_CALLBACK(mail_window_focus_cb), NULL);
+
+		gtk_dialog_add_button(GTK_DIALOG(dialog),
+					 _("Open All Messages"), GTK_RESPONSE_ACCEPT);
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						 PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES);
+
+		gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog->treeview), FALSE);
+
+		gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog->treeview), PIDGIN_MAIL_TEXT);
+		gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog->treeview),
+			             pidgin_tree_view_search_equal_func, NULL, NULL);
+
+		g_signal_connect(G_OBJECT(dialog), "response",
+						 G_CALLBACK(email_response_cb), spec_dialog);
+		g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview))),
+						 "changed", G_CALLBACK(selection_changed_cb), spec_dialog);
+		g_signal_connect(G_OBJECT(spec_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+
+		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, TRUE);
+		gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		label = gtk_label_new(NULL);
+		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
+
+	} else if (type == PIDGIN_NOTIFY_POUNCE) {
+		gtk_window_set_title(GTK_WINDOW(dialog), _("New Pounces"));
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						_("Dismiss"), GTK_RESPONSE_NO);
+		gtk_widget_set_sensitive(button, FALSE);
+		spec_dialog->dismiss_button = button;
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						PIDGIN_STOCK_EDIT, GTK_RESPONSE_APPLY);
+		gtk_widget_set_sensitive(button, FALSE);
+		spec_dialog->edit_button = button;
+
+		g_signal_connect(G_OBJECT(dialog), "response",
+						 G_CALLBACK(pounce_response_cb), spec_dialog);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Buddy"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+
+		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_POUNCE_ICON, NULL);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_ALIAS);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Event"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_EVENT);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Message"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_TEXT);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Date"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_DATE);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		label = gtk_label_new(NULL);
+		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have pounced!</span>"));
+
+		sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview));
+		gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+		g_signal_connect(G_OBJECT(sel), "changed",
+			G_CALLBACK(pounce_row_selected_cb), NULL);
+	}
+
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 2);
+
+	if (type == PIDGIN_NOTIFY_MAIL)
+		mail_dialog = spec_dialog;
+	else if (type == PIDGIN_NOTIFY_POUNCE) {
+		pounce_dialog = spec_dialog;
+        }
+
+	return spec_dialog->dialog;
+
+}
+
+void
+pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce,
+		const char *alias, const char *event, const char *message, const char *date)
+{
+	GtkWidget *dialog;
+	GdkPixbuf *icon;
+	GtkTreeIter iter;
+	PidginNotifyPounceData *pounce_data;
+
+	dialog = pidgin_get_notification_dialog(PIDGIN_NOTIFY_POUNCE);
+
+	icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+
+	pounce_data = g_new(PidginNotifyPounceData, 1);
+
+	pounce_data->account = account;
+	pounce_data->pounce = pounce;
+
+	gtk_tree_store_append(pounce_dialog->treemodel, &iter, NULL);
+
+	gtk_tree_store_set(pounce_dialog->treemodel, &iter,
+			PIDGIN_POUNCE_ICON, icon,
+			PIDGIN_POUNCE_ALIAS, alias,
+			PIDGIN_POUNCE_EVENT, event,
+			PIDGIN_POUNCE_TEXT, (message != NULL)? message : _("No message"),
+			PIDGIN_POUNCE_DATE, date,
+			PIDGIN_POUNCE_DATA, pounce_data,
+			-1);
+
+	if (icon)
+		g_object_unref(icon);
+
+	gtk_widget_show_all(dialog);
+
+	return;
+}
+
+static GtkWidget *
+pidgin_get_notification_dialog(PidginNotifyType type)
+{
+	GtkTreeStore *model = NULL;
+
+	if (type == PIDGIN_NOTIFY_MAIL) {
+		if (mail_dialog != NULL)
+			return mail_dialog->dialog;
+
+		model = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL,
+						GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
+
+	} else if (type == PIDGIN_NOTIFY_POUNCE) {
+
+		if (pounce_dialog != NULL)
+			return pounce_dialog->dialog;
+
+		model = gtk_tree_store_new(PIDGIN_POUNCE_COLUMNS,
+				GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+				G_TYPE_STRING, G_TYPE_POINTER);
+	}
+
+	return pidgin_get_dialog(type, model);
+}
+
 static PurpleNotifyUiOps ops =
 {
 	pidgin_notify_message,
--- a/pidgin/gtknotify.h	Wed Mar 11 21:49:08 2009 +0000
+++ b/pidgin/gtknotify.h	Fri Mar 20 00:34:17 2009 +0000
@@ -27,6 +27,18 @@
 #define _PIDGINNOTIFY_H_
 
 #include "notify.h"
+#include "pounce.h"
+
+/**
+ * Adds a buddy pounce to the buddy pounce dialog
+ *
+ * @param alias		The buddy alias
+ * @param event 	Event description
+ * @param message	Pounce message
+ * @param date		Pounce date
+ */
+void pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce,
+		const char *alias, const char *event, const char *message, const char *date);
 
 /**
  * Returns the UI operations structure for GTK+ notification functions.
--- a/pidgin/gtkpounce.c	Wed Mar 11 21:49:08 2009 +0000
+++ b/pidgin/gtkpounce.c	Fri Mar 20 00:34:17 2009 +0000
@@ -30,7 +30,6 @@
 #include "account.h"
 #include "conversation.h"
 #include "debug.h"
-#include "notify.h"
 #include "prpl.h"
 #include "request.h"
 #include "server.h"
@@ -41,6 +40,7 @@
 #include "gtkdialogs.h"
 #include "gtkimhtml.h"
 #include "gtkpounce.h"
+#include "gtknotify.h"
 #include "pidginstock.h"
 #include "gtkutils.h"
 
@@ -1275,7 +1275,6 @@
 	/* Handle double-clicking */
 	g_signal_connect(G_OBJECT(treeview), "button_press_event",
 					 G_CALLBACK(pounce_double_click_cb), dialog);
-
 	gtk_container_add(GTK_CONTAINER(sw), treeview);
 	gtk_widget_show(treeview);
 
@@ -1458,27 +1457,27 @@
 		 */
 		tmp = g_strdup_printf(
 				   (events & PURPLE_POUNCE_TYPING) ?
-				   _("%s has started typing to you (%s)") :
+				   _("Started typing") :
 				   (events & PURPLE_POUNCE_TYPED) ?
-				   _("%s has paused while typing to you (%s)") :
+				   _("Paused while typing") :
 				   (events & PURPLE_POUNCE_SIGNON) ?
-				   _("%s has signed on (%s)") :
+				   _("Signed on") :
 				   (events & PURPLE_POUNCE_IDLE_RETURN) ?
-				   _("%s has returned from being idle (%s)") :
+				   _("Returned from being idle") :
 				   (events & PURPLE_POUNCE_AWAY_RETURN) ?
-				   _("%s has returned from being away (%s)") :
+				   _("Returned from being away") :
 				   (events & PURPLE_POUNCE_TYPING_STOPPED) ?
-				   _("%s has stopped typing to you (%s)") :
+				   _("Stopped typing") :
 				   (events & PURPLE_POUNCE_SIGNOFF) ?
-				   _("%s has signed off (%s)") :
+				   _("Signed off") :
 				   (events & PURPLE_POUNCE_IDLE) ?
-				   _("%s has become idle (%s)") :
+				   _("Became idle") :
 				   (events & PURPLE_POUNCE_AWAY) ?
-				   _("%s has gone away. (%s)") :
+				   _("Went away") :
 				   (events & PURPLE_POUNCE_MESSAGE_RECEIVED) ?
-				   _("%s has sent you a message. (%s)") :
-				   _("Unknown pounce event. Please report this!"),
-				   alias, purple_account_get_protocol_name(account));
+				   _("Sent a message") :
+				   _("Unknown.... Please report this!")
+				   );
 
 		/*
 		 * Ok here is where I change the second argument, title, from
@@ -1488,16 +1487,9 @@
 		if ((name_shown = purple_account_get_alias(account)) == NULL)
 			name_shown = purple_account_get_username(account);
 
-		if (reason == NULL)
-		{
-			purple_notify_info(NULL, name_shown, tmp, purple_date_format_full(NULL));
-		}
-		else
-		{
-			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, purple_date_format_full(NULL));
-			purple_notify_info(NULL, name_shown, tmp, tmp2);
-			g_free(tmp2);
-		}
+		pidgin_notify_pounce_add(account, pounce, alias, tmp, reason,
+				purple_date_format_full(NULL));
+
 		g_free(tmp);
 	}