changeset 3517:6b0cb60162f4

[gaim-migrate @ 3590] Rob McQueen added a mute feature to his nice little docklet. I added a queuing feature. Configure the docklet in the plugins dialog to queue unread messages, and when you receive a message the docklet will eat it up and show a little message pending icon. Click on it, and read your message. ICQ people will like it. I also made plugin_event use a va_list. I bet this breaks perl. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Mon, 16 Sep 2002 08:35:24 +0000
parents db00eb77997d
children 5de4a27d7513
files ChangeLog TODO pixmaps/Makefile.am plugins/SIGNALS plugins/docklet/docklet.c po/POTFILES.in src/aim.c src/away.c src/buddy.c src/buddy_chat.c src/conversation.c src/core.h src/gaim.h src/idle.c src/module.c src/multi.c src/multi.h src/perl.c src/server.c src/ui.h
diffstat 20 files changed, 296 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Sep 16 07:04:55 2002 +0000
+++ b/ChangeLog	Mon Sep 16 08:35:24 2002 +0000
@@ -23,7 +23,7 @@
 	  Uekawa)
 	* Optionally uniquely colorize nicks in chats
 	* Changed some default options
-	* Updated desktop icon.
+	* Updated desktop and window icons (Thanks Robert McQueen)
 	* GTK2 Goodness:
 		- Preferences dialog
 		- Plugins dialog (Ari Pollak)
@@ -70,7 +70,10 @@
 	* /topic with no argument displays the current topic (Thanks Mark 
 	  Doliner)
 	* i18n fixes (Thanks Matt Wilson)
-	* Changed Ctrl-C color binding to Ctrl-K
+	* Changed Ctrl-C color removed
+	* Docklet plugin--replaces the old GNOME applet.  You'll need the
+	  Panel Notification Area applet for GNOME 2, or the patch from
+	  RedHat for KDE 3. (Thanks Robert McQueen)
 
 version 0.59 (06/24/2002):
 	* Hungarian translation added (Thanks, Sutto Zoltan)
--- a/TODO	Mon Sep 16 07:04:55 2002 +0000
+++ b/TODO	Mon Sep 16 08:35:24 2002 +0000
@@ -1,6 +1,5 @@
 THE CORE:
 	GPG Encryption of messages
-	Have plugin_event use varargs instead of void*
 	Separate core functions from UI stuff.
 		about.c, applet.[ch], away.c, browser.c, buddy.c, conversation.c,
 		convo.h, dialogs.c, gtk*, plugins.c, prefs.c, prpl.c, sound.c,
--- a/pixmaps/Makefile.am	Mon Sep 16 07:04:55 2002 +0000
+++ b/pixmaps/Makefile.am	Mon Sep 16 08:35:24 2002 +0000
@@ -133,7 +133,7 @@
 gaimdialogpix_DATA = gaim_error.png gaim_info.png gaim_question.png gaim_warning.png
 
 gaimdistpixdir = $(datadir)/pixmaps/gaim
-gaimdistpix_DATA = away.png connect.png msgpend.png offline.png online.png
+gaimdistpix_DATA = away.png connect.png msgpend.png offline.png online.png msgunread.png
 
 distpixmapdir = $(datadir)/pixmaps
 distpixmap_DATA = gaim.png
--- a/plugins/SIGNALS	Mon Sep 16 07:04:55 2002 +0000
+++ b/plugins/SIGNALS	Mon Sep 16 08:35:24 2002 +0000
@@ -29,6 +29,7 @@
 	event_chat_send_invite,
 	event_got_typing,
 	event_del_conversation,
+	event_connecting,
 };
 
 To add a signal handler, call the fuction gaim_signal_connect with the
@@ -295,7 +296,7 @@
 	won't be displayed at all.
 
 event_im_displayed_rcvd:
-	struct gaim_connection *gc, char *who, char *what, guint32 flags
+	struct gaim_connection *gc, char *who, char *what, guint32 flags, time_t time
 
 	This is called after what you receive is displayed. This is useful
 	for displaying an autoresponse after the message that triggered it.
@@ -306,6 +307,8 @@
 	'who' is who sent the message.
 	'what' is what was sent.
 	'flags' is flags on the message.
+	'time' is the time the message was received--it may be very different from the
+	 time this signal gets called
 
 event_chat_send_invite:
 	struct gaim_connection *gc, int id, char *who, char **msg
@@ -332,6 +335,7 @@
 
 	'gc' 	is the connection the typing is sent to.
 	'who' 	is the person typing to you.
+
 event_del_conversation:
 	struct conversation *c
 
@@ -341,3 +345,15 @@
 	anything.
 
 	'c'	is he conversation being closed.
+	
+event_connecting
+	struct aim_user *u
+
+	This is called when Gaim attempts to bring a user on-line. The
+	boolean u->connecting is set to true, and a global counter
+	incremented. The attempt can end with event_signon or event_signoff
+	being called, depending upon whether the attempt was a sucess or
+	a failure. In both cases, u->connecting is set to false and the
+	counter decremented.
+	
+	'u'	is the account being connected
--- a/plugins/docklet/docklet.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/plugins/docklet/docklet.c	Mon Sep 16 08:35:24 2002 +0000
@@ -23,9 +23,7 @@
     - don't crash when the plugin gets unloaded (it seems to crash after
        the plugin has gone, when gtk updates the button in the plugins
        dialog. backtrace is always useless. weird)
-    - have a toggle on the menu to mute all Gaim sounds
     - handle and update tooltips to show your current accounts
-    - connecting status support (needs more fruxing with the core)
     - dernyi's account status menu in the right click
     - store icons in gtk2 stock icon thing (needs doing for the whole prog)
     - pop up notices when GNOME2's system-tray-applet supports it, with a
@@ -34,17 +32,16 @@
 /* includes */
 #define GAIM_PLUGINS
 #include <gtk/gtk.h>
+#include <fcntl.h>
 #include "gaim.h"
 #include "eggtrayicon.h"
 
-/* macros */
-#define DOCKLET_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED)
-
 /* types */
 enum docklet_status {
 	online,
 	away,
 	away_pending,
+	unread_pending,
 	connecting,
 	offline
 };
@@ -56,6 +53,7 @@
 static EggTrayIcon *docklet = NULL;
 static GtkWidget *icon;
 static enum docklet_status status;
+static GtkWidget *configwin = NULL;
 
 static void docklet_embedded(GtkWidget *widget, void *data) {
 	debug_printf("Docklet: embedded\n");
@@ -68,35 +66,17 @@
 	docklet_create();
 }
 
-static void docklet_toggle() {
-	/* this looks bad, but we need to use (un)hide_buddy_list to allow buddy.c to
-	   correctly hide/iconify depending on the docklet refcount, and to reposition
-	   the blist for us when we unhide. no such constraint for the login window
-	   because nothing else needs a unified way to hide/iconify it. otherwise I'd
-	   make a function and use it for both. */
-	if (connections) {
-		if (GTK_WIDGET_VISIBLE(blist)) {
-			if (DOCKLET_WINDOW_ICONIFIED(blist)) {
-				unhide_buddy_list();
-			} else {
-				hide_buddy_list();
-			}
-		} else {
-			unhide_buddy_list();
-		}
-	} else {
-		if (GTK_WIDGET_VISIBLE(mainwindow)) {
-			if (DOCKLET_WINDOW_ICONIFIED(mainwindow)) {
-				gtk_window_present(GTK_WINDOW(mainwindow));
-			} else {
-				gtk_widget_hide(mainwindow);
-			}
-		} else {
-			gtk_window_present(GTK_WINDOW(mainwindow));
-		}
-	}
+ 
+static void docklet_mute(GtkWidget *toggle, void *data) {
+       mute_sounds = GTK_CHECK_MENU_ITEM(toggle)->active;
+       if (mute_sounds) {
+	       debug_printf("Docklet: sounds muted\n");
+       } else {
+               debug_printf("Docklet: sounds unmuted\n");
+       }
 }
 
+
 static void docklet_menu(GdkEventButton *event) {
 	static GtkWidget *menu = NULL;
 	GtkWidget *entry;
@@ -154,6 +134,11 @@
 	entry = gtk_separator_menu_item_new();
 	gtk_menu_append(GTK_MENU(menu), entry);
 
+	entry = gtk_check_menu_item_new_with_label(_("Mute Sounds"));
+	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), mute_sounds);
+	g_signal_connect(GTK_WIDGET(entry), "toggled", G_CALLBACK(docklet_mute), NULL);
+	gtk_menu_append(GTK_MENU(menu), entry);
+
 	entry = gtk_menu_item_new_with_label(_("Accounts"));
 	g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(account_editor), NULL);
 	gtk_menu_append(GTK_MENU(menu), entry);
@@ -184,7 +169,13 @@
 static void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data) {
 	switch (event->button) {
 		case 1:
-			docklet_toggle();
+			if (unread_message_queue) {
+ 				purge_away_queue(unread_message_queue);
+				unread_message_queue=NULL;
+				docklet_update_status();
+			}
+			else
+				docklet_toggle();
 			break;
 		case 2:
 			break;
@@ -208,8 +199,12 @@
 		case away_pending:
 			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgpend.png", NULL);
 			break;
+	        case unread_pending:
+			/* XXX MAKE ME BLINK! */
+			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgunread.png", NULL);
+			break;
 		case connecting:
-			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "connecting.png", NULL);
+			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "connect.png", NULL);
 			break;
 		case offline:
 			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "offline.png", NULL);
@@ -239,7 +234,9 @@
 	oldstatus = status;
 
 	if (connections) {
-		if (awaymessage) {
+		if (unread_message_queue) {
+			status = unread_pending;
+		} else if (awaymessage) {
 			if (message_queue) {
 				status = away_pending;
 			} else {
@@ -249,7 +246,11 @@
 			status = online;
 		}
 	} else {
-		status = offline;
+		if (connecting_count) {
+			status = connecting;
+		} else {
+			status = offline;
+		}
 	}
 
 	if (status != oldstatus) {
@@ -340,6 +341,33 @@
 	return NULL;
 }
 
+static void toggle_queue (GtkWidget *w, void *null) {
+	away_options ^= OPT_AWAY_QUEUE_UNREAD;
+	save_prefs();
+}
+	
+void gaim_plugin_config() {
+	/* This is the sorriest dialog ever written ever */
+	/* It's a good thing I plan on rewriting it later tonight */
+	GtkWidget *button;
+	GtkWidget *vbox;
+
+	if (configwin) return;
+	GAIM_DIALOG(configwin);
+
+	vbox = gtk_vbox_new(0, 6);
+	gtk_container_add(GTK_CONTAINER(configwin), vbox);
+	gtk_window_set_title(GTK_WINDOW(configwin), "Docklet Configuration");
+	
+	button = gtk_check_button_new_with_mnemonic("_Hide new messages until docklet is clicked");
+	gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), away_options & OPT_AWAY_QUEUE_UNREAD);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(toggle_queue), NULL);
+	gtk_box_pack_end(GTK_BOX(vbox), button, 0, 0, 0);
+
+	gtk_widget_show_all(configwin);
+}
+
+
 void gaim_plugin_remove() {
 	if (GTK_WIDGET_VISIBLE(docklet)) {
 		docklet_remove();
--- a/po/POTFILES.in	Mon Sep 16 07:04:55 2002 +0000
+++ b/po/POTFILES.in	Mon Sep 16 08:35:24 2002 +0000
@@ -1,3 +1,4 @@
+plugins/docklet/docklet.c
 plugins/chatlist.c
 plugins/gtik.c
 src/protocols/gg/gg.c
--- a/src/aim.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/aim.c	Mon Sep 16 08:35:24 2002 +0000
@@ -63,6 +63,7 @@
 GSList *away_messages = NULL;
 GList *conversations = NULL;
 GSList *message_queue = NULL;
+GSList *unread_message_queue = NULL;
 GSList *away_time_queue = NULL;
 
 GtkWidget *mainwindow = NULL;
@@ -98,7 +99,7 @@
 {
 #ifdef GAIM_PLUGINS
 	/* first we tell those who have requested it we're quitting */
-	plugin_event(event_quit, 0, 0, 0, 0);
+	plugin_event(event_quit);
 
 	/* then we remove everyone in a mass suicide */
 	remove_all_plugins();
--- a/src/away.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/away.c	Mon Sep 16 08:35:24 2002 +0000
@@ -53,15 +53,15 @@
 	imaway = NULL;
 }
 
-void purge_away_queue()
+void purge_away_queue(GSList *queue)
 {
 	struct conversation *cnv;
 
 	gtk_clist_freeze(GTK_CLIST(clistqueue));
 	gtk_clist_clear(GTK_CLIST(clistqueue));
 
-	while (message_queue) {
-		struct queued_message *qm = message_queue->data;
+	while (queue) {
+		struct queued_message *qm = queue->data;
 
 		cnv = find_conversation(qm->name);
 		if (!cnv)
@@ -70,7 +70,7 @@
 			set_convo_gc(cnv, qm->gc);
 		write_to_conv(cnv, qm->message, qm->flags, NULL, qm->tm, qm->len);
 
-		message_queue = g_slist_remove(message_queue, qm);
+		queue = g_slist_remove(queue, qm);
 
 		g_free(qm->message);
 		g_free(qm);
@@ -134,7 +134,7 @@
 	} else {
 		gtk_widget_hide(clistqueue);
 		gtk_widget_hide(clistqueuesw);
-		purge_away_queue();
+		purge_away_queue(message_queue);
 	}
 }
 
@@ -143,7 +143,7 @@
 	if (imaway) {
 		GtkWidget *tmp = imaway;
 
-		purge_away_queue();
+		purge_away_queue(message_queue);
 
 		imaway = NULL;
 		gtk_widget_destroy(tmp);
--- a/src/buddy.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/buddy.c	Mon Sep 16 08:35:24 2002 +0000
@@ -110,8 +110,7 @@
 };
 static GSList *shows = NULL;
 
-static gboolean blist_hidden;
-static int docklet_refcount = 0;
+static int docklet_count = 0;
 
 /* Predefine some functions */
 static void new_bp_callback(GtkWidget *w, struct buddy *bs);
@@ -703,7 +702,7 @@
 		}
 
 		/* we send the menu widget so we can add menuitems within a plugin */
-		plugin_event(event_draw_menu, menu, b->name, 0, 0);
+		plugin_event(event_draw_menu, menu, b->name);
 
 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
 
@@ -1400,7 +1399,7 @@
 void do_quit()
 {
 	/* first we tell those who have requested it we're quitting */
-	plugin_event(event_quit, 0, 0, 0, 0);
+	plugin_event(event_quit);
 
 	signoff_all();
 #ifdef GAIM_PLUGINS
@@ -1993,10 +1992,39 @@
 	return g;
 }
 
+
+void docklet_toggle() {
+	/* Useful for the docklet plugin and also for the win32 tray icon*/
+	/* This is called when one of those is clicked--it will show/hide the 
+	   buddy list/login window--depending on which is active */
+	if (connections) {
+		if (GTK_WIDGET_VISIBLE(blist)) {
+			if (DOCKLET_WINDOW_ICONIFIED(blist)) {
+				unhide_buddy_list();
+			} else {
+				hide_buddy_list();
+			}
+		} else {
+			unhide_buddy_list();
+		}
+	} else {
+		if (GTK_WIDGET_VISIBLE(mainwindow)) {
+			if (DOCKLET_WINDOW_ICONIFIED(mainwindow)) {
+				gtk_window_present(GTK_WINDOW(mainwindow));
+			} else {
+				gtk_widget_hide(mainwindow);
+			}
+		} else {
+			gtk_window_present(GTK_WINDOW(mainwindow));
+		}
+	}
+}
+
+
 /* used by this file, and by iconaway.so */
 void hide_buddy_list() {
 	if (blist) {
-		if (!connections || docklet_refcount) {
+		if (!connections || docklet_count) {
 			gtk_widget_hide(blist);
 		} else {
 			gtk_window_iconify(GTK_WINDOW(blist));
@@ -2020,7 +2048,7 @@
 
 /* for the delete_event handler */
 static void close_buddy_list() {
-	if (docklet_refcount) {
+	if (docklet_count) {
 		hide_buddy_list();
 	} else {
 		do_quit();
@@ -2028,16 +2056,16 @@
 }
 
 void docklet_add() {
-	docklet_refcount++;
-	printf("docklet_refcount: %d\n",docklet_refcount);
+	docklet_count++;
+	printf("docklet_count: %d\n",docklet_count);
 }
 
 void docklet_remove() {
-	if (docklet_refcount) {
-		docklet_refcount--;
+	if (docklet_count) {
+		docklet_count--;
 	}
-	printf("docklet_refcount: %d\n",docklet_refcount);
-	if (!docklet_refcount) {
+	printf("docklet_count: %d\n",docklet_count);
+	if (!docklet_count) {
 		if (connections) {
 			unhide_buddy_list();
 		} else {
--- a/src/buddy_chat.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/buddy_chat.c	Mon Sep 16 08:35:24 2002 +0000
@@ -955,7 +955,7 @@
 	char tmp[BUF_LONG];
 	int pos;
 
-	plugin_event(event_chat_buddy_join, b->gc, (void *)b->id, name, 0);
+	plugin_event(event_chat_buddy_join, b->gc, b->id, name);
 	b->in_room = g_list_insert_sorted(b->in_room, name, insertname);
 	pos = g_list_index(b->in_room, name);
 
@@ -1048,7 +1048,7 @@
 
 	char tmp[BUF_LONG];
 
-	plugin_event(event_chat_buddy_leave, b->gc, (void *)b->id, buddy, 0);
+	plugin_event(event_chat_buddy_leave, b->gc, b->id, buddy);
 
 	while (names) {
 		if (!g_strcasecmp((char *)names->data, buddy)) {
--- a/src/conversation.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/conversation.c	Mon Sep 16 08:35:24 2002 +0000
@@ -194,7 +194,7 @@
 	update_icon(c);
 	update_checkbox(c);
 	update_smilies(c);
-	plugin_event(event_new_conversation, name, 0, 0, 0);
+	plugin_event(event_new_conversation, name);
 	return c;
 }
 
@@ -259,7 +259,7 @@
 
 void delete_conversation(struct conversation *c)
 {
-	plugin_event(event_del_conversation, c, 0, 0, 0);
+	plugin_event(event_del_conversation, c);
 	conversations = g_list_remove(conversations, c);
 	if (c->fg_color_dialog)
 		gtk_widget_destroy(c->fg_color_dialog);
@@ -949,13 +949,6 @@
 				toggle_font(c->font, c);
 				gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event");
 				break;
-			case 'k':
-			case 'K':
-				quiet_set(c->fgcolorbtn,
-					  !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(c->fgcolorbtn)));
-				toggle_fg_color(c->fgcolorbtn, c);
-				gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event");
-				break;
 			}
 		}
 		if (convo_options & OPT_CONVO_CTL_SMILEYS) {
@@ -1188,7 +1181,7 @@
 		enum gaim_event evnt = c->is_chat ? event_chat_send : event_im_send;
 		int plugin_return = plugin_event(evnt, c->gc,
 						 c->is_chat ? (void *)c->id : c->name,
-						 &buffy, 0);
+						 &buffy);
 		if (!buffy) {
 			g_free(buf2);
 			g_free(buf);
@@ -1210,7 +1203,7 @@
 		gboolean binary = FALSE;
 
 		buffy = g_strdup(buf);
-		plugin_event(event_im_displayed_sent, c->gc, c->name, &buffy, 0);
+		plugin_event(event_im_displayed_sent, c->gc, c->name, &buffy);
 		if (buffy) {
 			int imflags = 0;
 			if (c->check && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(c->check)))
--- a/src/core.h	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/core.h	Mon Sep 16 08:35:24 2002 +0000
@@ -26,6 +26,7 @@
 #include <config.h>
 #endif
 
+#include <sys/types.h>
 #ifdef HAVE_ICONV
 #include <iconv.h>
 #endif
@@ -189,7 +190,7 @@
 #ifdef USE_PERL
 extern void perl_autoload();
 extern void perl_end();
-extern int perl_event(enum gaim_event, void *, void *, void *, void *);
+extern int perl_event(enum gaim_event, void *, void *, void *, void *, void *);
 extern int perl_load_file(char *);
 extern void unload_perl_scripts();
 extern void list_perl_scripts();
@@ -205,7 +206,7 @@
 extern void gaim_plugin_unload(GModule *);
 extern void remove_all_plugins();
 #endif
-extern int plugin_event(enum gaim_event, void *, void *, void *, void *);
+extern int plugin_event(enum gaim_event, ...);
 extern char *event_name(enum gaim_event);
 
 /* Functions in server.c */
--- a/src/gaim.h	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/gaim.h	Mon Sep 16 08:35:24 2002 +0000
@@ -333,12 +333,14 @@
 #define OPT_AWAY_NO_AUTO_RESP		0x00000010
 #define OPT_AWAY_QUEUE			0x00000020
 #define OPT_AWAY_IDLE_RESP		0x00000040
+#define OPT_AWAY_QUEUE_UNREAD           0x00000060
 
 extern guint away_resend;
 extern int report_idle;
 extern int web_browser;
 extern GSList *aim_users;
 extern GSList *message_queue;
+extern GSList *unread_message_queue;
 extern GSList *away_time_queue;
 extern char sound_cmd[2048];
 extern char web_command[2048];
@@ -351,6 +353,7 @@
 extern void do_pounce(struct gaim_connection *, char *, int);
 void create_prpl_icon(GtkWidget *widget, struct gaim_connection *gc,
 					  GdkPixmap **pixmap, GdkBitmap **mask);
+void docklet_toggle();
 
 /* Functions in buddy_chat.c */
 extern void show_new_buddy_chat(struct conversation *);
--- a/src/idle.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/idle.c	Mon Sep 16 08:35:24 2002 +0000
@@ -50,7 +50,7 @@
 	/* Not idle, really...  :) */
 	update_idle_times();
 
-	plugin_event(event_blist_update, 0, 0, 0, 0);
+	plugin_event(event_blist_update);
 
 	time(&t);
 
--- a/src/module.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/module.c	Mon Sep 16 08:35:24 2002 +0000
@@ -376,144 +376,35 @@
 	return buf;
 }
 
-static void debug_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4)
-{
-	if (!opt_debug && !(misc_options & OPT_MISC_DEBUG))
-		return;
-
-	switch (event) {
-		case event_blist_update:
-			/* this happens *really* often */
-			if (opt_debug) {
-				debug_printf("%s\n", event_name(event));
-			}
-			break;
-	        case event_quit:
-			debug_printf("%s\n", event_name(event));
-			break;
-		case event_signon:
-		case event_signoff:
-			debug_printf("%s: %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username);
-			break;
-		case event_new_conversation:
-			debug_printf("%s: %s\n", event_name(event), (char *)arg1);
-			break;
-		case event_error:
-			debug_printf("%s: %d\n", event_name(event), (int)arg1);
-			break;
-		case event_buddy_signon:
-		case event_buddy_signoff:
-		case event_buddy_away:
-		case event_buddy_back:
-		case event_buddy_idle:
-	        case event_buddy_unidle:
-	        case event_set_info:
-	        case event_got_typing:
-			debug_printf("%s: %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username, (char *)arg2);
-			break;
-		case event_chat_leave:
-			debug_printf("%s: %s %d\n", event_name(event),
-					((struct gaim_connection *)arg1)->username, (int)arg2);
-			break;
-		case event_im_send:
-		case event_im_displayed_sent:
-			debug_printf("%s: %s %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(char *)arg2, *(char **)arg3 ? *(char **)arg3 : "");
-			break;
-		case event_chat_join:
-		case event_chat_buddy_join:
-		case event_chat_buddy_leave:
-			debug_printf("%s: %s %d %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(int)arg2, (char *)arg3);
-			break;
-		case event_chat_send:
-			debug_printf("%s: %s %d %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(int)arg2, *(char **)arg3 ? *(char **)arg3 : "");
-			break;
-		case event_away:
-			debug_printf("%s: %s %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(char *)arg2, (char *)arg3 ? (char *)arg3 : "");
-			break;
-		case event_warned:
-			debug_printf("%s: %s %s %d\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(char *)arg2 ? (char *)arg2 : "", (int)arg3);
-			break;
-		case event_im_recv:
-			debug_printf("%s: %s %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					*(char **)arg2 ? *(char **)arg2 : "",
-					*(char **)arg3 ? *(char **)arg3 : "");
-			break;
-		case event_im_displayed_rcvd:
-			debug_printf("%s: %s %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(char *)arg2 ? (char *)arg2 : "",
-					(char *)arg3 ? (char *)arg3 : "");
-			break;
-		case event_chat_recv:
-			debug_printf("%s: %s %d %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(int)arg2,
-					*(char **)arg3 ? *(char **)arg3 : "",
-					*(char **)arg4 ? *(char **)arg4 : "");
-			break;
-		case event_chat_send_invite:
-			debug_printf("%s: %s %d %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(int)arg2, (char *)arg3,
-					*(char **)arg4 ? *(char **)arg4 : "");
-			break;
-		case event_chat_invited:
-			debug_printf("%s: %s %s %s %s\n", event_name(event),
-					((struct gaim_connection *)arg1)->username,
-					(char *)arg2, (char *)arg3,
-					(char *)arg4 ? (char *)arg4 : "");
-			break;
-		case event_del_conversation:
-			debug_printf("%s: %s\n", event_name(event), (char *)arg1);
-			break;
-		case event_connecting:
-			debug_printf("%s: %s\n", event_name(event), ((struct aim_user *)arg1)->username);
-			break;
-		default:
-			debug_printf("%s: um, right. yeah.\n", event_name(event));
-			break;
-	}
-}
-
-int plugin_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4)
+int plugin_event(enum gaim_event event, ...)
 {
 #ifdef GAIM_PLUGINS
 	GList *c = callbacks;
 	struct gaim_callback *g;
+	va_list arrg;
+	void    *arg1 = NULL, 
+		*arg2 = NULL,
+		*arg3 = NULL, 
+		*arg4 = NULL,
+		*arg5 = NULL;
 #endif
 
-	debug_event(event, arg1, arg2, arg3, arg4);
+	debug_printf("%s\n", event_name(event));
 
 #ifdef GAIM_PLUGINS
 	while (c) {
-		void (*zero)(void *);
-		void (*one)(void *, void *);
-		void (*two)(void *, void *, void *);
-		void (*three)(void *, void *, void *, void *);
-		void (*four)(void *, void *, void *, void *, void *);
-
+		void (*cbfunc)(void *, ...);
+		
 		g = (struct gaim_callback *)c->data;
 		if (g->event == event && g->function != NULL) {
+			cbfunc=g->function;
+			va_start(arrg, event);
 			switch (event) {
 
 				/* no args */
 			case event_blist_update:
 			case event_quit:
-				zero = g->function;
-				zero(g->data);
+				cbfunc(g->data);
 				break;
 
 				/* one arg */
@@ -523,8 +414,8 @@
 			case event_del_conversation:
 			case event_error:
 			case event_connecting:
-				one = g->function;
-				one(arg1, g->data);
+				arg1 = va_arg(arrg, void *);
+				cbfunc(arg1, g->data);
 				break;
 
 				/* two args */
@@ -534,48 +425,101 @@
 			case event_buddy_back:
 			case event_buddy_idle:
 			case event_buddy_unidle:
-			case event_chat_leave:
 			case event_set_info:
 			case event_draw_menu:
 			case event_got_typing:
-				two = g->function;
-				two(arg1, arg2, g->data);
+				arg1 = va_arg(arrg, void *);
+				arg2 = va_arg(arrg, void *);
+				cbfunc(arg1, arg2, g->data);
 				break;
-
+			case event_chat_leave:
+				{
+					int id;
+					arg1 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					cbfunc(arg1, id, g->data);
+				}
+				break;
 				/* three args */
 			case event_im_send:
 			case event_im_displayed_sent:
-			case event_chat_join:
+			case event_away:
+				arg1 = va_arg(arrg, void *);
+				arg2 = va_arg(arrg, void *);
+				arg3 = va_arg(arrg, void *);
+				cbfunc(arg1, arg2, arg3, g->data);
+				break;
 			case event_chat_buddy_join:
 			case event_chat_buddy_leave:
 			case event_chat_send:
-			case event_away:
-			case event_back:
+			case event_chat_join:
+				{
+					int id;
+					arg1 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					arg3 = va_arg(arrg, void*);
+					cbfunc(arg1, id, arg3, g->data);
+				}
+				break;
 			case event_warned:
-				three = g->function;
-				three(arg1, arg2, arg3, g->data);
+				{
+					int id;
+					arg1 = va_arg(arrg, void*);
+					arg2 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					cbfunc(arg1, arg2, id, g->data);
+				}
 				break;
-
 				/* four args */
 			case event_im_recv:
+			case event_chat_invited:
+				arg1 = va_arg(arrg, void *);
+				arg2 = va_arg(arrg, void *);
+				arg3 = va_arg(arrg, void *);
+				arg4 = va_arg(arrg, void *);
+				cbfunc(arg1, arg2, arg3, arg4, g->data);
+				break;
 			case event_chat_recv:
-			case event_im_displayed_rcvd:
 			case event_chat_send_invite:
-			case event_chat_invited:
-				four = g->function;
-				four(arg1, arg2, arg3, arg4, g->data);
+				{
+					int id;
+					arg1 = va_arg(arrg, void *);
+					id = va_arg(arrg, int);
+
+					arg3 = va_arg(arrg, void *);
+					arg4 = va_arg(arrg, void *);
+					cbfunc(arg1, id, arg3, arg4, g->data);
+				}
 				break;
-
-			default:
+				/* five args */
+			case event_im_displayed_rcvd:
+				{
+					time_t time;
+					arg1 = va_arg(arrg, void *);
+					arg2 = va_arg(arrg, void *);
+					arg3 = va_arg(arrg, void *);
+					arg4 = va_arg(arrg, void *);
+					time = va_arg(arrg, time_t);
+					cbfunc(arg1, arg2, arg3, arg4, time, g->data);
+				}
+				break;
+				default:
 				debug_printf("unknown event %d\n", event);
 				break;
 			}
+			va_end(arrg);
 		}
 		c = c->next;
 	}
 #endif /* GAIM_PLUGINS */
 #ifdef USE_PERL
-	return perl_event(event, arg1, arg2, arg3, arg4);
+	va_start(arrg, event);
+	arg1 = va_arg(arrg, void *);
+	arg2 = va_arg(arrg, void *);
+	arg3 = va_arg(arrg, void *);
+	arg4 = va_arg(arrg, void *);
+	arg5 = va_arg(arrg, void *);
+	return perl_event(event, arg1, arg2, arg3, arg4, arg5);
 #else
 	return 0;
 #endif
--- a/src/multi.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/multi.c	Mon Sep 16 08:35:24 2002 +0000
@@ -40,6 +40,7 @@
 #define LOGIN_STEPS 5
 
 GSList *connections;
+int connecting_count = 0;
 
 static GtkWidget *acctedit = NULL;
 static GtkWidget *list = NULL;	/* the clist of names in the accteditor */
@@ -1156,7 +1157,9 @@
 	gaim_setup(gc);
 
 	gc->user->connecting = FALSE;
-	plugin_event(event_signon, gc, 0, 0, 0);
+	connecting_count--;
+		
+	plugin_event(event_signon, gc);
 	system_log(log_signon, gc, NULL, OPT_LOG_BUDDY_SIGNON | OPT_LOG_MY_SIGNON);
 
 	/* away option given? */
@@ -1445,12 +1448,15 @@
 	/* remove this here so plugins get a sensible count of connections */
 	connections = g_slist_remove(connections, gc);
 	debug_printf("date: %s\n", full_date());
-	plugin_event(event_signoff, gc, 0, 0, 0);
+	plugin_event(event_signoff, gc);
 	system_log(log_signoff, gc, NULL, OPT_LOG_BUDDY_SIGNON | OPT_LOG_MY_SIGNON);
 	/* set this in case the plugin died before really connecting.
 	   do it after calling the plugins so they can determine if
 	   this user was ever on-line or not */
-	gc->user->connecting = FALSE;
+	if (gc->user->connecting) {
+		gc->user->connecting = FALSE;
+		connecting_count--;
+	}
 	serv_close(gc);
 
 	/* more UI stuff */
--- a/src/multi.h	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/multi.h	Mon Sep 16 08:35:24 2002 +0000
@@ -98,6 +98,9 @@
 /* now that we have our struct, we're going to need lots of them. Maybe even a list of them. */
 extern GSList *connections;
 
+/* number of accounts that are currently in the process of connecting */
+extern int connecting_count;
+
 struct aim_user *new_user(const char *, int, int);
 struct gaim_connection *new_gaim_conn(struct aim_user *);
 void destroy_gaim_conn(struct gaim_connection *);
--- a/src/perl.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/perl.c	Mon Sep 16 08:35:24 2002 +0000
@@ -662,7 +662,7 @@
 	XSRETURN(0);
 }
 
-int perl_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4)
+int perl_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
 {
 	char *buf = NULL;
 	GList *handler;
@@ -754,6 +754,7 @@
 				arg2 ? escape_quotes(arg2) : "", (int)arg3);
 		break;
 	case event_quit:
+	case event_blist_update:
 		buf = g_malloc0(1);
 		break;
 	case event_new_conversation:
@@ -780,6 +781,9 @@
 		g_free(tmp3);
 		}
 		break;
+	case event_draw_menu:
+		/* we can't handle this usefully without gtk/perl bindings */
+		return 0;
 	default:
 		debug_printf("someone forgot to handle %s in the perl binding\n", event_name(event));
 		return 0;
--- a/src/server.c	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/server.c	Mon Sep 16 08:35:24 2002 +0000
@@ -55,7 +55,8 @@
 
 		debug_printf(PACKAGE " " VERSION " logging in %s using %s\n", user->username, p->name());
 		user->connecting = TRUE;
-		plugin_event(event_connecting, user, 0, 0, 0);
+		connecting_count++;
+		plugin_event(event_connecting, user);
 		p->login(user);
 	}
 }
@@ -207,7 +208,7 @@
 
 		gc->prpl->set_away(gc, state, buf);
 
-		plugin_event(event_away, gc, state, buf, 0);
+		plugin_event(event_away, gc, state, buf);
 
 		if (buf)
 			g_free(buf);
@@ -231,7 +232,7 @@
 void serv_set_info(struct gaim_connection *g, char *info)
 {
 	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->set_info) {
-		if (plugin_event(event_set_info, g, info, 0, 0))
+		if (plugin_event(event_set_info, g, info))
 			return;
 		g->prpl->set_info(g, info);
 	}
@@ -523,7 +524,7 @@
 		buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
 		strcpy(buffy, message);
 		angel = g_strdup(name);
-		plugin_return = plugin_event(event_im_recv, gc, &angel, &buffy, (void *)&flags);
+		plugin_return = plugin_event(event_im_recv, gc, &angel, &buffy, &flags);
 
 		if (!buffy || !angel || plugin_return) {
 			if (buffy)
@@ -687,21 +688,34 @@
 		/* we're not away. this is easy. if the convo window doesn't exist, create and update
 		 * it (if it does exist it was updated earlier), then play a sound indicating we've
 		 * received it and then display it. easy. */
-		if (cnv == NULL) {
-			cnv = new_conversation(name);
-			set_convo_gc(cnv, gc);
+		if (away_options & OPT_AWAY_QUEUE_UNREAD && !find_conversation(name)) {
+			/* We're gonna queue it up and wait for the user to ask for it... probably
+			 * by clicking the docklet or windows tray icon. */
+			struct queued_message *qm;
+			qm = g_new0(struct queued_message, 1);
+			g_snprintf(qm->name, sizeof(qm->name), "%s", name);
+			qm->message = g_strdup(message);
+			qm->gc = gc;
+			qm->tm = mtime;
+			qm->flags = away | WFLAG_RECV;
+			qm->len = len;
+			unread_message_queue = g_slist_append(unread_message_queue, qm);
+		} else {
+			if (cnv == NULL) {
+				cnv = new_conversation(name);
+				set_convo_gc(cnv, gc);
+			}
+			if (new_conv && (sound_options & OPT_SOUND_FIRST_RCV))
+				play_sound(SND_FIRST_RECEIVE);
+			else if (cnv->makesound)
+				play_sound(SND_RECEIVE);
+			
+			set_convo_name(cnv, name);
+			
+			write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime, len);
 		}
-		if (new_conv && (sound_options & OPT_SOUND_FIRST_RCV))
-			play_sound(SND_FIRST_RECEIVE);
-		else if (cnv->makesound)
-			play_sound(SND_RECEIVE);
-
-		set_convo_name(cnv, name);
-
-		write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime, len);
 	}
-
-	plugin_event(event_im_displayed_rcvd, gc, name, message, (void *)flags);
+	plugin_event(event_im_displayed_rcvd, gc, name, message, flags, mtime);
 	g_free(name);
 	g_free(message);
 }
@@ -739,12 +753,12 @@
 	}
 
 	if (!b->idle && idle) {
-		plugin_event(event_buddy_idle, gc, b->name, 0, 0);
+		plugin_event(event_buddy_idle, gc, b->name);
 		system_log(log_idle, gc, b, OPT_LOG_BUDDY_IDLE);
 	}
 	if (b->idle && !idle) {
 		do_pounce(gc, b->name, OPT_POUNCE_UNIDLE);
-		plugin_event(event_buddy_unidle, gc, b->name, 0, 0);
+		plugin_event(event_buddy_unidle, gc, b->name);
 		system_log(log_unidle, gc, b, OPT_LOG_BUDDY_IDLE);
 	}
 
@@ -753,10 +767,10 @@
 
 	if ((b->uc & UC_UNAVAILABLE) && !(type & UC_UNAVAILABLE)) {
 		do_pounce(gc, b->name, OPT_POUNCE_UNAWAY);
-		plugin_event(event_buddy_back, gc, b->name, 0, 0);
+		plugin_event(event_buddy_back, gc, b->name);
 		system_log(log_back, gc, b, OPT_LOG_BUDDY_AWAY);
 	} else if (!(b->uc & UC_UNAVAILABLE) && (type & UC_UNAVAILABLE)) {
-		plugin_event(event_buddy_away, gc, b->name, 0, 0);
+		plugin_event(event_buddy_away, gc, b->name);
 		system_log(log_away, gc, b, OPT_LOG_BUDDY_AWAY);
 	}
 
@@ -770,12 +784,12 @@
 		if (!b->present) {
 			b->present = 1;
 			do_pounce(gc, b->name, OPT_POUNCE_SIGNON);
-			plugin_event(event_buddy_signon, gc, b->name, 0, 0);
+			plugin_event(event_buddy_signon, gc, b->name);
 			system_log(log_signon, gc, b, OPT_LOG_BUDDY_SIGNON);
 		}
 	} else {
 		if (b->present) {
-			plugin_event(event_buddy_signoff, gc, b->name, 0, 0);
+			plugin_event(event_buddy_signoff, gc, b->name);
 			system_log(log_signoff, gc, b, OPT_LOG_BUDDY_SIGNON);
 		}
 		b->present = 0;
@@ -789,7 +803,7 @@
 {
 	char buf2[1024];
 
-	plugin_event(event_warned, gc, name, (void *)lev, 0);
+	plugin_event(event_warned, gc, name, lev);
 
 	if (gc->evil >= lev) {
 		gc->evil = lev;
@@ -810,7 +824,7 @@
 		 set_convo_gc(cnv, gc);
 		 show_typing(cnv);
 	} else return;
-	 plugin_event(event_got_typing, gc, name, 0, 0);
+	 plugin_event(event_got_typing, gc, name);
 	 do_pounce(gc, name, OPT_POUNCE_TYPING);
 	 if (timeout > 0) {
 		 if (cnv->typing_timeout)
@@ -916,7 +930,7 @@
 {
 	struct conversation *b;
 
-	plugin_event(event_chat_join, gc, (void *)id, name, 0);
+	plugin_event(event_chat_join, gc, id, name);
 
 	b = (struct conversation *)g_new0(struct conversation, 1);
 	gc->buddy_chats = g_slist_append(gc->buddy_chats, b);
@@ -975,7 +989,7 @@
 	if (!b)
 		return;
 
-	plugin_event(event_chat_leave, g, (void *)b->id, 0, 0);
+	plugin_event(event_chat_leave, g, b->id);
 
 	debug_printf("Leaving room %s.\n", b->name);
 
@@ -1014,7 +1028,7 @@
 	buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
 	strcpy(buffy, message);
 	angel = g_strdup(who);
-	plugin_return = plugin_event(event_chat_recv, g, (void *)b->id, &angel, &buffy);
+	plugin_return = plugin_event(event_chat_recv, g, b->id, &angel, &buffy);
 
 	if (!buffy || !angel || plugin_return) {
 		if (buffy)
--- a/src/ui.h	Mon Sep 16 07:04:55 2002 +0000
+++ b/src/ui.h	Mon Sep 16 08:35:24 2002 +0000
@@ -36,6 +36,8 @@
 #define gtk_accel_group_attach(x, y) _gtk_accel_group_attach(x, y)
 #define gtk_widget_lock_accelerators(x)
 
+#define DOCKLET_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED)
+
 #define DEFAULT_FONT_FACE "Helvetica"
 
 #define BROWSER_NETSCAPE              0
@@ -318,7 +320,7 @@
 extern void away_list_unclicked(GtkWidget *, struct away_message *);
 extern void away_list_clicked(GtkWidget *, struct away_message *);
 extern void toggle_away_queue();
-extern void purge_away_queue();
+extern void purge_away_queue(GSList*);
 
 /* Functions in browser.c */
 extern void open_url(GtkWidget *, char *);