changeset 5875:448f2f4ca3ec

[gaim-migrate @ 6307] Rewrote the pounce framework.. yes, again. But now it saves! OOhh!! Neato. Things are cleaner too. AND I CHANGED THE OK BUTTON TO SAVE! committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Sun, 15 Jun 2003 02:03:23 +0000
parents 964e4f94fc56
children 8d6e5f804325
files src/account.c src/gaimrc.c src/gtkpounce.c src/gtkpounce.h src/main.c src/pounce.c src/pounce.h
diffstat 7 files changed, 560 insertions(+), 176 deletions(-) [+]
line wrap: on
line diff
--- a/src/account.c	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/account.c	Sun Jun 15 02:03:23 2003 +0000
@@ -705,7 +705,7 @@
 	AccountParserData *data = user_data;
 
 	if (data->buffer != NULL)
-		g_free(data->buffer);
+		g_string_free(data->buffer, TRUE);
 
 	if (data->setting_name != NULL)
 		g_free(data->setting_name);
--- a/src/gaimrc.c	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/gaimrc.c	Sun Jun 15 02:03:23 2003 +0000
@@ -1647,7 +1647,7 @@
 
 		old_pounce_opts_to_new(ph->options, &events, &actions);
 
-		pounce = gaim_gtkpounce_new(account, ph->name, events);
+		pounce = gaim_pounce_new(GAIM_GTK_UI, account, ph->name, events);
 
 		gaim_pounce_action_set_enabled(pounce, "open-window",
 			(actions & GAIM_GTKPOUNCE_OPEN_WIN));
--- a/src/gtkpounce.c	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/gtkpounce.c	Sun Jun 15 02:03:23 2003 +0000
@@ -219,7 +219,8 @@
 	if (*sound   == '\0') sound   = NULL;
 
 	if (dialog->pounce == NULL) {
-		dialog->pounce = gaim_gtkpounce_new(dialog->account, name, events);
+		dialog->pounce = gaim_pounce_new(GAIM_GTK_UI, dialog->account,
+										 name, events);
 	}
 	else {
 		gaim_pounce_set_events(dialog->pounce, events);
@@ -251,6 +252,8 @@
 
 	delete_win_cb(NULL, NULL, dialog);
 
+	gaim_pounces_sync();
+
 	/* Rebuild the pounce menu */
 	blist = gaim_get_blist();
 
@@ -316,134 +319,6 @@
 	return opt_menu;
 }
 
-static void
-pounce_cb(GaimPounce *pounce, GaimPounceEvent events, void *data)
-{
-	GaimConversation *conv;
-	GaimAccount *account;
-	const char *pouncee;
-
-	pouncee = gaim_pounce_get_pouncee(pounce);
-	account = gaim_pounce_get_pouncer(pounce);
-
-	if (gaim_pounce_action_is_enabled(pounce, "open-window")) {
-		conv = gaim_find_conversation(pouncee);
-
-		if (conv == NULL)
-			conv = gaim_conversation_new(GAIM_CONV_IM, account, pouncee);
-	}
-
-	if (gaim_pounce_action_is_enabled(pounce, "popup-notify")) {
-		char tmp[1024];
-
-		g_snprintf(tmp, sizeof(tmp),
-				   (events & GAIM_POUNCE_TYPING) ? _("%s has started typing to you") :
-				   (events & GAIM_POUNCE_SIGNON) ? _("%s has signed on") :
-				   (events & GAIM_POUNCE_IDLE_RETURN) ? _("%s has returned from being idle") :
-				   (events & GAIM_POUNCE_AWAY_RETURN) ? _("%s has returned from being away") :
-				   (events & GAIM_POUNCE_TYPING_STOPPED) ? _("%s has stopped typing to you") :
-				   (events & GAIM_POUNCE_SIGNOFF) ? _("%s has signed off") :
-				   (events & GAIM_POUNCE_IDLE) ? _("%s has become idle") :
-				   (events & GAIM_POUNCE_AWAY) ? _("%s has gone away.") :
-				   _("Unknown pounce event. Please report this!"),
-				   pouncee);
-
-		gaim_notify_info(NULL, NULL, tmp, NULL);
-	}
-
-	if (gaim_pounce_action_is_enabled(pounce, "send-message")) {
-		const char *message;
-
-		message = gaim_pounce_action_get_attribute(pounce, "send-message",
-												   "message");
-
-		if (message != NULL) {
-			conv = gaim_find_conversation(pouncee);
-
-			if (conv == NULL)
-				conv = gaim_conversation_new(GAIM_CONV_IM, account, pouncee);
-
-			gaim_conversation_write(conv, NULL, message, -1,
-									WFLAG_SEND, time(NULL));
-
-			serv_send_im(account->gc, (char *)pouncee, (char *)message, -1, 0);
-		}
-	}
-
-#ifndef _WIN32
-	if (gaim_pounce_action_is_enabled(pounce, "execute-command")) {
-		const char *command;
-
-		command = gaim_pounce_action_get_attribute(pounce, "execute-command",
-												   "command");
-
-		if (command != NULL) {
-			int pid = fork();
-
-			if (pid == 0) {
-				char *args[4];
-
-				args[0] = "sh";
-				args[1] = "-c";
-				args[2] = (char *)command;
-				args[3] = NULL;
-
-				execvp(args[0], args);
-
-				_exit(0);
-			}
-		}
-	}
-#endif /* _WIN32 */
-
-	if (gaim_pounce_action_is_enabled(pounce, "play-sound")) {
-		const char *sound;
-
-		sound = gaim_pounce_action_get_attribute(pounce, "play-sound",
-												 "sound");
-
-		if (sound != NULL)
-			gaim_sound_play_file(sound);
-		else
-			gaim_sound_play_event(GAIM_SOUND_POUNCE_DEFAULT);
-	}
-}
-
-static void
-free_pounce(void *data)
-{
-	struct gaim_buddy_list *blist;
-	struct gaim_gtk_buddy_list *gtkblist;
-
-	/* Rebuild the pounce menu */
-	blist = gaim_get_blist();
-
-	if (GAIM_IS_GTK_BLIST(blist))
-	{
-		gtkblist = GAIM_GTK_BLIST(blist);
-
-		gaim_gtkpounce_menu_build(gtkblist->bpmenu);
-	}
-}
-
-GaimPounce *
-gaim_gtkpounce_new(GaimAccount *pouncer, const char *pouncee,
-				   GaimPounceEvent events)
-{
-	GaimPounce *pounce;
-
-	pounce = gaim_pounce_new(GAIM_GTK_UI, pouncer, pouncee, events,
-							 pounce_cb, NULL, free_pounce);
-
-	gaim_pounce_action_register(pounce, "open-window");
-	gaim_pounce_action_register(pounce, "popup-notify");
-	gaim_pounce_action_register(pounce, "send-message");
-	gaim_pounce_action_register(pounce, "execute-command");
-	gaim_pounce_action_register(pounce, "play-sound");
-
-	return pounce;
-}
-
 void
 gaim_gtkpounce_dialog_show(struct buddy *buddy,
 						   GaimPounce *cur_pounce)
@@ -729,8 +604,8 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(cancel_cb), dialog);
 
-	/* OK button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OK);
+	/* Save button */
+	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_show(button);
 	
@@ -910,3 +785,129 @@
 	fill_menu(menu, G_CALLBACK(edit_pounce_cb));
 }
 
+static void
+pounce_cb(GaimPounce *pounce, GaimPounceEvent events, void *data)
+{
+	GaimConversation *conv;
+	GaimAccount *account;
+	const char *pouncee;
+
+	pouncee = gaim_pounce_get_pouncee(pounce);
+	account = gaim_pounce_get_pouncer(pounce);
+
+	if (gaim_pounce_action_is_enabled(pounce, "open-window")) {
+		conv = gaim_find_conversation(pouncee);
+
+		if (conv == NULL)
+			conv = gaim_conversation_new(GAIM_CONV_IM, account, pouncee);
+	}
+
+	if (gaim_pounce_action_is_enabled(pounce, "popup-notify")) {
+		char tmp[1024];
+
+		g_snprintf(tmp, sizeof(tmp),
+				   (events & GAIM_POUNCE_TYPING) ? _("%s has started typing to you") :
+				   (events & GAIM_POUNCE_SIGNON) ? _("%s has signed on") :
+				   (events & GAIM_POUNCE_IDLE_RETURN) ? _("%s has returned from being idle") :
+				   (events & GAIM_POUNCE_AWAY_RETURN) ? _("%s has returned from being away") :
+				   (events & GAIM_POUNCE_TYPING_STOPPED) ? _("%s has stopped typing to you") :
+				   (events & GAIM_POUNCE_SIGNOFF) ? _("%s has signed off") :
+				   (events & GAIM_POUNCE_IDLE) ? _("%s has become idle") :
+				   (events & GAIM_POUNCE_AWAY) ? _("%s has gone away.") :
+				   _("Unknown pounce event. Please report this!"),
+				   pouncee);
+
+		gaim_notify_info(NULL, NULL, tmp, NULL);
+	}
+
+	if (gaim_pounce_action_is_enabled(pounce, "send-message")) {
+		const char *message;
+
+		message = gaim_pounce_action_get_attribute(pounce, "send-message",
+												   "message");
+
+		if (message != NULL) {
+			conv = gaim_find_conversation(pouncee);
+
+			if (conv == NULL)
+				conv = gaim_conversation_new(GAIM_CONV_IM, account, pouncee);
+
+			gaim_conversation_write(conv, NULL, message, -1,
+									WFLAG_SEND, time(NULL));
+
+			serv_send_im(account->gc, (char *)pouncee, (char *)message, -1, 0);
+		}
+	}
+
+#ifndef _WIN32
+	if (gaim_pounce_action_is_enabled(pounce, "execute-command")) {
+		const char *command;
+
+		command = gaim_pounce_action_get_attribute(pounce, "execute-command",
+												   "command");
+
+		if (command != NULL) {
+			int pid = fork();
+
+			if (pid == 0) {
+				char *args[4];
+
+				args[0] = "sh";
+				args[1] = "-c";
+				args[2] = (char *)command;
+				args[3] = NULL;
+
+				execvp(args[0], args);
+
+				_exit(0);
+			}
+		}
+	}
+#endif /* _WIN32 */
+
+	if (gaim_pounce_action_is_enabled(pounce, "play-sound")) {
+		const char *sound;
+
+		sound = gaim_pounce_action_get_attribute(pounce, "play-sound",
+												 "sound");
+
+		if (sound != NULL)
+			gaim_sound_play_file(sound);
+		else
+			gaim_sound_play_event(GAIM_SOUND_POUNCE_DEFAULT);
+	}
+}
+
+static void
+free_pounce(GaimPounce *pounce)
+{
+	struct gaim_buddy_list *blist;
+	struct gaim_gtk_buddy_list *gtkblist;
+
+	/* Rebuild the pounce menu */
+	blist = gaim_get_blist();
+
+	if (GAIM_IS_GTK_BLIST(blist))
+	{
+		gtkblist = GAIM_GTK_BLIST(blist);
+
+		gaim_gtkpounce_menu_build(gtkblist->bpmenu);
+	}
+}
+
+static void
+new_pounce(GaimPounce *pounce)
+{
+	gaim_pounce_action_register(pounce, "open-window");
+	gaim_pounce_action_register(pounce, "popup-notify");
+	gaim_pounce_action_register(pounce, "send-message");
+	gaim_pounce_action_register(pounce, "execute-command");
+	gaim_pounce_action_register(pounce, "play-sound");
+}
+
+void
+gaim_gtk_pounces_init(void)
+{
+	gaim_pounces_register_handler(GAIM_GTK_UI, pounce_cb, new_pounce,
+								  free_pounce);
+}
--- a/src/gtkpounce.h	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/gtkpounce.h	Sun Jun 15 02:03:23 2003 +0000
@@ -26,18 +26,6 @@
 #include "pounce.h"
 
 /**
- * Creates a GTK-specific pounce.
- *
- * @param pouncer The account that will pounce.
- * @param pouncee The buddy to pounce on.
- * @param events  The event(s) to pounce on.
- *
- * @return The new buddy pounce.
- */
-GaimPounce *gaim_gtkpounce_new(GaimAccount *pouncer, const char *pouncee,
-							   GaimPounceEvent events);
-
-/**
  * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
  *
  * @param buddy      The optional buddy to pounce on.
@@ -52,4 +40,9 @@
  */
 void gaim_gtkpounce_menu_build(GtkWidget *menu);
 
+/**
+ * Initializes the GTK+ pounces subsystem.
+ */
+void gaim_gtk_pounces_init(void);
+
 #endif /* _GAIM_GTK_POUNCE_H_ */
--- a/src/main.c	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/main.c	Sun Jun 15 02:03:23 2003 +0000
@@ -42,6 +42,7 @@
 #include "gtkdebug.h"
 #include "gtkft.h"
 #include "gtknotify.h"
+#include "gtkpounce.h"
 #include "gtkprefs.h"
 #include "gtkrequest.h"
 #include "gtksound.h"
@@ -855,8 +856,10 @@
 	gaim_conversation_init();
 	gaim_proxy_init();
 	gaim_sound_init();
+	gaim_pounces_init();
 
 	gaim_gtk_conversation_init();
+	gaim_gtk_pounces_init();
 
 	plugin_search_paths[0] = LIBDIR;
 	plugin_search_paths[1] = gaim_user_dir();
--- a/src/pounce.c	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/pounce.c	Sun Jun 15 02:03:23 2003 +0000
@@ -30,6 +30,23 @@
 
 typedef struct
 {
+	GString *buffer;
+
+	GaimPounce *pounce;
+	GaimPounceEvent events;
+
+	char *ui_name;
+	char *pouncee;
+	char *protocol_id;
+	char *event_type;
+	char *action_name;
+	char *param_name;
+	char *account_name;
+
+} PounceParserData;
+
+typedef struct
+{
 	char *name;
 
 	gboolean enabled;
@@ -38,10 +55,21 @@
 
 } GaimPounceActionData;
 
+typedef struct
+{
+	char *ui;
+	GaimPounceCb cb;
+	void (*new_pounce)(GaimPounce *);
+	void (*free_pounce)(GaimPounce *);
 
-static GList   *pounces = NULL;
-static guint    pounces_save_timer = 0;
-static gboolean pounces_loaded = FALSE;
+} GaimPounceHandler;
+
+
+static GHashTable *pounce_handlers = NULL;
+static GList      *pounces = NULL;
+static guint       pounces_save_timer = 0;
+static gboolean    pounces_loaded = FALSE;
+
 
 static GaimPounceActionData *
 find_action_data(const GaimPounce *pounce, const char *name)
@@ -86,16 +114,15 @@
 
 GaimPounce *
 gaim_pounce_new(const char *ui_type, GaimAccount *pouncer,
-				const char *pouncee, GaimPounceEvent event,
-				GaimPounceCb cb, void *data, void (*free)(void *))
+				const char *pouncee, GaimPounceEvent event)
 {
 	GaimPounce *pounce;
+	GaimPounceHandler *handler;
 
 	g_return_val_if_fail(ui_type != NULL, NULL);
 	g_return_val_if_fail(pouncer != NULL, NULL);
 	g_return_val_if_fail(pouncee != NULL, NULL);
 	g_return_val_if_fail(event   != 0,    NULL);
-	g_return_val_if_fail(cb      != NULL, NULL);
 
 	pounce = g_new0(GaimPounce, 1);
 
@@ -103,13 +130,15 @@
 	pounce->pouncer  = pouncer;
 	pounce->pouncee  = g_strdup(pouncee);
 	pounce->events   = event;
-	pounce->callback = cb;
-	pounce->data     = data;
-	pounce->free     = free;
 
 	pounce->actions  = g_hash_table_new_full(g_str_hash, g_str_equal,
 											 g_free, free_action_data);
 
+	handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type);
+
+	if (handler != NULL && handler->new_pounce != NULL)
+		handler->new_pounce(pounce);
+
 	pounces = g_list_append(pounces, pounce);
 
 	return pounce;
@@ -118,8 +147,12 @@
 void
 gaim_pounce_destroy(GaimPounce *pounce)
 {
+	GaimPounceHandler *handler;
+
 	g_return_if_fail(pounce != NULL);
 
+	handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type);
+
 	pounces = g_list_remove(pounces, pounce);
 
 	if (pounce->ui_type != NULL) g_free(pounce->ui_type);
@@ -127,8 +160,8 @@
 
 	g_hash_table_destroy(pounce->actions);
 
-	if (pounce->free != NULL && pounce->data != NULL)
-		pounce->free(pounce->data);
+	if (handler != NULL && handler->free_pounce != NULL)
+		handler->free_pounce(pounce);
 
 	g_free(pounce);
 
@@ -327,6 +360,7 @@
 					GaimPounceEvent events)
 {
 	GaimPounce *pounce;
+	GaimPounceHandler *handler;
 	GList *l, *l_next;
 
 	g_return_if_fail(pouncer != NULL);
@@ -341,8 +375,10 @@
 			(gaim_pounce_get_pouncer(pounce) == pouncer) &&
 			!strcmp(gaim_pounce_get_pouncee(pounce), pouncee)) {
 
-			if (pounce->callback != NULL) {
-				pounce->callback(pounce, events, gaim_pounce_get_data(pounce));
+			handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type);
+
+			if (handler != NULL && handler->cb != NULL) {
+				handler->cb(pounce, events, gaim_pounce_get_data(pounce));
 
 				if (!gaim_pounce_get_save(pounce))
 					gaim_pounce_destroy(pounce);
@@ -376,9 +412,299 @@
 	return NULL;
 }
 
+/* XML Stuff */
+static void
+free_parser_data(gpointer user_data)
+{
+	PounceParserData *data = user_data;
+
+	if (data->buffer != NULL)
+		g_string_free(data->buffer, TRUE);
+
+	if (data->ui_name      != NULL) g_free(data->ui_name);
+	if (data->pouncee      != NULL) g_free(data->pouncee);
+	if (data->protocol_id  != NULL) g_free(data->protocol_id);
+	if (data->event_type   != NULL) g_free(data->event_type);
+	if (data->action_name  != NULL) g_free(data->action_name);
+	if (data->param_name   != NULL) g_free(data->param_name);
+	if (data->account_name != NULL) g_free(data->account_name);
+
+	g_free(data);
+}
+
+static void
+start_element_handler(GMarkupParseContext *context,
+					  const gchar *element_name,
+					  const gchar **attribute_names,
+					  const gchar **attribute_values,
+					  gpointer user_data, GError **error)
+{
+	PounceParserData *data = user_data;
+	GHashTable *atts;
+	int i;
+
+	atts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+	for (i = 0; attribute_names[i] != NULL; i++) {
+		g_hash_table_insert(atts, g_strdup(attribute_names[i]),
+							g_strdup(attribute_values[i]));
+	}
+
+	if (data->buffer != NULL) {
+		g_string_free(data->buffer, TRUE);
+		data->buffer = NULL;
+	}
+
+	if (!strcmp(element_name, "pounce")) {
+		const char *ui = g_hash_table_lookup(atts, "ui");
+
+		if (ui == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Unset 'ui' parameter for pounce!\n");
+		}
+		else
+			data->ui_name = g_strdup(ui);
+
+		data->events = 0;
+	}
+	else if (!strcmp(element_name, "account")) {
+		const char *protocol_id = g_hash_table_lookup(atts, "protocol");
+
+		if (protocol_id == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Unset 'protocol' parameter for account!\n");
+		}
+		else
+			data->protocol_id = g_strdup(protocol_id);
+	}
+	else if (!strcmp(element_name, "event")) {
+		const char *type = g_hash_table_lookup(atts, "type");
+
+		if (type == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Unset 'type' parameter for event!\n");
+		}
+		else
+			data->event_type = g_strdup(type);
+	}
+	else if (!strcmp(element_name, "action")) {
+		const char *type = g_hash_table_lookup(atts, "type");
+
+		if (type == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Unset 'type' parameter for action!\n");
+		}
+		else
+			data->action_name = g_strdup(type);
+	}
+	else if (!strcmp(element_name, "param")) {
+		const char *param_name = g_hash_table_lookup(atts, "name");
+
+		if (param_name == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Unset 'name' parameter for param!\n");
+		}
+		else
+			data->param_name = g_strdup(param_name);
+	}
+
+	g_hash_table_destroy(atts);
+}
+
+static void
+end_element_handler(GMarkupParseContext *context, const gchar *element_name,
+					gpointer user_data,  GError **error)
+{
+	PounceParserData *data = user_data;
+	gchar *buffer = NULL;
+
+	if (data->buffer != NULL) {
+		buffer = g_string_free(data->buffer, FALSE);
+		data->buffer = NULL;
+	}
+
+	if (!strcmp(element_name, "account")) {
+		data->account_name = g_strdup(buffer);
+	}
+	else if (!strcmp(element_name, "pouncee")) {
+		data->pouncee = g_strdup(buffer);
+	}
+	else if (!strcmp(element_name, "event")) {
+		if (!strcmp(data->event_type, "sign-on"))
+			data->events |= GAIM_POUNCE_SIGNON;
+		else if (!strcmp(data->event_type, "sign-off"))
+			data->events |= GAIM_POUNCE_SIGNOFF;
+		else if (!strcmp(data->event_type, "away"))
+			data->events |= GAIM_POUNCE_AWAY;
+		else if (!strcmp(data->event_type, "return-from-away"))
+			data->events |= GAIM_POUNCE_AWAY_RETURN;
+		else if (!strcmp(data->event_type, "idle"))
+			data->events |= GAIM_POUNCE_IDLE;
+		else if (!strcmp(data->event_type, "return-from-idle"))
+			data->events |= GAIM_POUNCE_IDLE_RETURN;
+		else if (!strcmp(data->event_type, "typing"))
+			data->events |= GAIM_POUNCE_TYPING;
+		else if (!strcmp(data->event_type, "stop-typing"))
+			data->events |= GAIM_POUNCE_TYPING_STOPPED;
+
+		g_free(data->event_type);
+		data->event_type = NULL;
+	}
+	else if (!strcmp(element_name, "action")) {
+		gaim_pounce_action_set_enabled(data->pounce, data->action_name, TRUE);
+
+		g_free(data->action_name);
+		data->action_name = NULL;
+	}
+	else if (!strcmp(element_name, "param")) {
+		gaim_pounce_action_set_attribute(data->pounce, data->action_name,
+										 data->param_name, buffer);
+
+		g_free(data->param_name);
+		data->param_name = NULL;
+	}
+	else if (!strcmp(element_name, "events")) {
+		GList *l;
+		GaimAccount *account;
+		GaimProtocol protocol = -1;
+
+		for (l = gaim_plugins_get_protocols(); l != NULL; l = l->next) {
+			GaimPlugin *plugin = (GaimPlugin *)l->data;
+
+			if (GAIM_IS_PROTOCOL_PLUGIN(plugin)) {
+				if (!strcmp(plugin->info->id, data->protocol_id)) {
+					protocol = GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol;
+
+					break;
+				}
+			}
+		}
+
+		account = gaim_accounts_find(data->account_name, protocol);
+
+		g_free(data->account_name);
+		g_free(data->protocol_id);
+
+		data->account_name = NULL;
+		data->protocol_id  = NULL;
+
+		if (account == NULL) {
+			gaim_debug(GAIM_DEBUG_ERROR, "pounce",
+					   "Account for pounce not found!\n");
+		}
+		else {
+			gaim_debug(GAIM_DEBUG_INFO, "pounce",
+					   "Creating pounce: %s, %s\n", data->ui_name,
+					   data->pouncee);
+
+			data->pounce = gaim_pounce_new(data->ui_name, account,
+										   data->pouncee, data->events);
+		}
+
+		g_free(data->pouncee);
+		data->pouncee = NULL;
+	}
+	else if (!strcmp(element_name, "pounce")) {
+		data->pounce = NULL;
+		data->events = 0;
+
+		if (data->ui_name      != NULL) g_free(data->ui_name);
+		if (data->pouncee      != NULL) g_free(data->pouncee);
+		if (data->protocol_id  != NULL) g_free(data->protocol_id);
+		if (data->event_type   != NULL) g_free(data->event_type);
+		if (data->action_name  != NULL) g_free(data->action_name);
+		if (data->param_name   != NULL) g_free(data->param_name);
+		if (data->account_name != NULL) g_free(data->account_name);
+
+		data->ui_name      = NULL;
+		data->pounce       = NULL;
+		data->protocol_id  = NULL;
+		data->event_type   = NULL;
+		data->action_name  = NULL;
+		data->param_name   = NULL;
+		data->account_name = NULL;
+	}
+
+	if (buffer != NULL)
+		g_free(buffer);
+}
+
+static void
+text_handler(GMarkupParseContext *context, const gchar *text,
+			 gsize text_len, gpointer user_data, GError **error)
+{
+	PounceParserData *data = user_data;
+
+	if (data->buffer == NULL)
+		data->buffer = g_string_new_len(text, text_len);
+	else
+		g_string_append_len(data->buffer, text, text_len);
+}
+
+static GMarkupParser pounces_parser =
+{
+	start_element_handler,
+	end_element_handler,
+	text_handler,
+	NULL,
+	NULL
+};
+
 gboolean
 gaim_pounces_load(void)
 {
+	gchar *filename = g_build_filename(gaim_user_dir(), "pounces.xml", NULL);
+	gchar *contents = NULL;
+	gsize length;
+	GMarkupParseContext *context;
+	GError *error = NULL;
+	PounceParserData *parser_data;
+
+	if (filename == NULL) {
+		pounces_loaded = TRUE;
+		return FALSE;
+	}
+
+	if (!g_file_get_contents(filename, &contents, &length, &error)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "pounces",
+				   "Error reading pounces: %s\n", error->message);
+
+		g_error_free(error);
+
+		pounces_loaded = TRUE;
+		return FALSE;
+	}
+
+	parser_data = g_new0(PounceParserData, 1);
+
+	context = g_markup_parse_context_new(&pounces_parser, 0,
+										 parser_data, free_parser_data);
+
+	if (!g_markup_parse_context_parse(context, contents, length, NULL)) {
+		g_markup_parse_context_free(context);
+		g_free(contents);
+
+		pounces_loaded = TRUE;
+
+		return FALSE;
+	}
+
+	if (!g_markup_parse_context_end_parse(context, NULL)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "pounces", "Error parsing %s\n",
+				   filename);
+
+		g_markup_parse_context_free(context);
+		g_free(contents);
+		pounces_loaded = TRUE;
+
+		return FALSE;
+	}
+
+	g_markup_parse_context_free(context);
+	g_free(contents);
+
+	g_free(filename);
+
 	pounces_loaded = TRUE;
 
 	return TRUE;
@@ -444,21 +770,21 @@
 	fprintf(fp, "  <events>\n");
 
 	if (events & GAIM_POUNCE_SIGNON)
-		fprintf(fp, "   <event type='sign-on'>\n");
+		fprintf(fp, "   <event type='sign-on'/>\n");
 	if (events & GAIM_POUNCE_SIGNOFF)
-		fprintf(fp, "   <event type='sign-off'>\n");
+		fprintf(fp, "   <event type='sign-off'/>\n");
 	if (events & GAIM_POUNCE_AWAY)
-		fprintf(fp, "   <event type='away'>\n");
+		fprintf(fp, "   <event type='away'/>\n");
 	if (events & GAIM_POUNCE_AWAY_RETURN)
-		fprintf(fp, "   <event type='return-from-away'>\n");
+		fprintf(fp, "   <event type='return-from-away'/>\n");
 	if (events & GAIM_POUNCE_IDLE)
-		fprintf(fp, "   <event type='idle'>\n");
+		fprintf(fp, "   <event type='idle'/>\n");
 	if (events & GAIM_POUNCE_IDLE_RETURN)
-		fprintf(fp, "   <event type='return-from-idle'>\n");
+		fprintf(fp, "   <event type='return-from-idle'/>\n");
 	if (events & GAIM_POUNCE_TYPING)
-		fprintf(fp, "   <event type='start-typing'>\n");
+		fprintf(fp, "   <event type='start-typing'/>\n");
 	if (events & GAIM_POUNCE_TYPING_STOPPED)
-		fprintf(fp, "   <event type='stop-typing'>\n");
+		fprintf(fp, "   <event type='stop-typing'/>\n");
 
 	fprintf(fp, "  </events>\n");
 	fprintf(fp, "  <actions>\n");
@@ -530,8 +856,52 @@
 	g_free(filename_real);
 }
 
+void
+gaim_pounces_register_handler(const char *ui, GaimPounceCb cb,
+							  void (*new_pounce)(GaimPounce *pounce),
+							  void (*free_pounce)(GaimPounce *pounce))
+{
+	GaimPounceHandler *handler;
+
+	g_return_if_fail(ui != NULL);
+	g_return_if_fail(cb != NULL);
+
+	handler = g_new0(GaimPounceHandler, 1);
+
+	handler->ui          = g_strdup(ui);
+	handler->cb          = cb;
+	handler->new_pounce  = new_pounce;
+	handler->free_pounce = free_pounce;
+
+	g_hash_table_insert(pounce_handlers, g_strdup(ui), handler);
+}
+
+void
+gaim_pounces_unregister_handler(const char *ui)
+{
+	g_return_if_fail(ui != NULL);
+
+	g_hash_table_remove(pounce_handlers, ui);
+}
+
 GList *
 gaim_pounces_get_all(void)
 {
 	return pounces;
 }
+
+static void
+free_pounce_handler(gpointer user_data)
+{
+	GaimPounceHandler *handler = (GaimPounceHandler *)user_data;
+
+	g_free(handler->ui);
+	g_free(handler);
+}
+
+void
+gaim_pounces_init(void)
+{
+	pounce_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
+											g_free, free_pounce_handler);
+}
--- a/src/pounce.h	Sat Jun 14 23:50:24 2003 +0000
+++ b/src/pounce.h	Sun Jun 15 02:03:23 2003 +0000
@@ -69,9 +69,6 @@
 
 	gboolean save;                /**< Whether or not the pounce should
 	                                   be saved after activation. */
-	GaimPounceCb callback;        /**< The callback function to call when the
-	                                   event is triggered.        */
-	void (*free)(void *data);     /**< The data free function.    */
 	void *data;                   /**< Pounce-specific data.      */
 };
 
@@ -82,16 +79,11 @@
  * @param pouncer The account that will pounce.
  * @param pouncee The buddy to pounce on.
  * @param event   The event(s) to pounce on.
- * @param cb      The callback function to call when the pounce is triggered.
- * @param data    Pounce-specific data.
- * @param free    The function to free the pounce-specific data.
  *
  * @return The new buddy pounce structure.
  */
-GaimPounce *gaim_pounce_new(const char *ui_type,
-							GaimAccount *pouncer, const char *pouncee,
-							GaimPounceEvent event, GaimPounceCb cb,
-							void *data, void (*free)(void *));
+GaimPounce *gaim_pounce_new(const char *ui_type, GaimAccount *pouncer,
+							const char *pouncee, GaimPounceEvent event);
 
 /**
  * Destroys a buddy pounce.
@@ -263,6 +255,12 @@
 GaimPounce *gaim_find_pounce(const GaimAccount *pouncer,
 							 const char *pouncee, GaimPounceEvent events);
 
+
+/**
+ * Initializes the pounces subsystem.
+ */
+void gaim_pounces_init(void);
+
 /**
  * Loads the pounces.
  */
@@ -274,6 +272,25 @@
 void gaim_pounces_sync(void);
 
 /**
+ * Registers a pounce handler for a UI.
+ *
+ * @param ui          The UI name.
+ * @param cb          The callback function.
+ * @param new_pounce  The function called when a pounce is created.
+ * @param free_pounce The function called when a pounce is freed.
+ */
+void gaim_pounces_register_handler(const char *ui, GaimPounceCb cb,
+								   void (*new_pounce)(GaimPounce *pounce),
+								   void (*free_pounce)(GaimPounce *pounce));
+
+/**
+ * Unregisters a pounce handle for a UI.
+ *
+ * @param ui The UI name.
+ */
+void gaim_pounces_unregister_handler(const char *ui);
+
+/**
  * Returns a list of all registered buddy pounces.
  *
  * @return The list of buddy pounces.