[evolution-patches] HIG errors for mailer monster patch




"Team",

This is a snapshot of the work i've been doing - i branched evo as notzed-eerror-branch for it too.  This isn't fully complete yet, and not widely tested, however it should provide hig'd errors for:

- filter stuff
- all mailer errors and questions (incl get password, etc).
- most composer errors
- camel errors, even if the text itself isn't hig yet.

It does a lot more than the error list from Ben provided alternate text for, although not everything he listed - yet.  But now error text (apart from camel) is quite separate from the code and can be changed in most cases independently of it.

For the error text, instead of using ${n} i borrowed the C# idea of just using {n}.  So you can't put { in errors anywhere (or at least in the formats), but the numbering scheme saves a lot of bother with repeating values or re-using values in different fields.  Except all the arguments must be strings (all the mailer errors were strings anyway).  Hence as jpr asked - no issue with plurals, you can do that in code if required and pass it as a string.  I also extended the code a bit from the blog entry so it can do substitution in window titles too.

Does i18n, blah blah etc.  Although i'm not that fond of the way i18n is done, but intltool does "strange things" (from what i can tell, a mixture of gettext requiring translations for params and in-built xml:lang alternatives for elements), and the code should work (even if its not that efficient, though scanning 10 language alternates 5 times for 50 messages wasn't even a fly wing's flap on a C=64).

And if it isn't fully hig (i copied the glade file exactly though), or heaven forbid, the hig changes, it can be fixed in one place.

Well, I was almost "Big Kev Excited" about this yesterday, but maybe i'm not so sure anymore - it does the job, saves code (particularly with the HIG targetted buttons stuff), and decouples the interface from the code (so non-coders can tweak and test, without source), so it can't all be bad.

PS might've missed something in the patch (but nobody's gonna try it anyway).

Michael Zucchi <notzed ximian com>

Ximian Evolution and Free Software Developer


Novell, Inc.
Index: camel/ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/camel/ChangeLog,v
retrieving revision 1.2101
diff -u -3 -r1.2101 ChangeLog
--- camel/ChangeLog	14 Apr 2004 18:19:33 -0000	1.2101
+++ camel/ChangeLog	16 Apr 2004 09:11:24 -0000
@@ -1,3 +1,9 @@
+2004-04-16  Not Zed  <NotZed Ximian com>
+
+	* camel-exception.c (camel_exception_set_extended): Add an
+	extended error description field to the exception.  This is a
+	little experimental.
+
 2004-04-14  Jeffrey Stedfast  <fejj ximian com>
 
 	* providers/imap/camel-imap-store.c
Index: camel/camel-exception.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-exception.c,v
retrieving revision 1.27
diff -u -3 -r1.27 camel-exception.c
--- camel/camel-exception.c	2 Apr 2004 09:53:29 -0000	1.27
+++ camel/camel-exception.c	16 Apr 2004 09:11:24 -0000
@@ -233,6 +233,37 @@
 }
 
 /**
+ * camel_exception_set_extended:
+ * @ex: 
+ * @fmt: 
+ * 
+ * Set extended exception information.
+ **/
+void camel_exception_set_extended(CamelException *ex, const char *fmt, ...)
+{
+	va_list args;
+	char *old;
+
+	if (camel_debug("exception"))
+		printf("CamelException.set_extended(%p, '%s')\n", ex, fmt);
+	
+	if (!ex)
+		return;
+
+	CAMEL_EXCEPTION_LOCK(exception);
+	
+	old = ex->extended;
+	
+	va_start(args, fmt);
+	ex->extended = g_strdup_vprintf (fmt, args);
+	va_end (args);
+
+	g_free (old);
+
+	CAMEL_EXCEPTION_UNLOCK(exception);
+}
+
+/**
  * camel_exception_xfer: transfer an exception
  * @ex_dst: Destination exception object 
  * @ex_src: Source exception object
Index: camel/camel-exception.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-exception.h,v
retrieving revision 1.17
diff -u -3 -r1.17 camel-exception.h
--- camel/camel-exception.h	27 Oct 2001 16:59:27 -0000	1.17
+++ camel/camel-exception.h	16 Apr 2004 09:11:24 -0000
@@ -41,16 +41,13 @@
 
 } ExceptionId;
 
-
 struct _CamelException {
 	/* do not access the fields directly */
 	ExceptionId id;
 	char *desc;
-
+	char *extended;
 };
 
-
-
 /* creation and destruction functions */
 CamelException *          camel_exception_new           (void);
 void                      camel_exception_free          (CamelException *exception);
@@ -66,6 +63,7 @@
 							 ExceptionId id,
 							 const char *format,  
 							 ...);
+void camel_exception_set_extended(CamelException *ex, const char *fmt, ...);
 
 
 /* exception content transfer */
Index: composer/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/composer/Makefile.am,v
retrieving revision 1.68
diff -u -3 -r1.68 Makefile.am
--- composer/Makefile.am	3 Feb 2004 17:32:16 -0000	1.68
+++ composer/Makefile.am	16 Apr 2004 09:11:26 -0000
@@ -29,6 +29,11 @@
 
 ##
 
+error_in_files = mail-composer-errors.xml.in
+error_DATA = $(error_in_files:.xml.in=.xml)
+errordir = $(privdatadir)/errors
+ INTLTOOL_XML_RULE@
+
 idl_DATA = $(IDLS)
 
 glade_DATA =					\
@@ -77,11 +82,12 @@
 	listener.h
 
 EXTRA_DIST =					\
+	$(error_in_files)			\
 	$(glade_DATA)				\
 	$(IDLS)					\
 	ChangeLog.pre-1-4
 
-BUILT_SOURCES = $(IDL_GENERATED) $(HTML_EDITOR_GENERATED)
+BUILT_SOURCES = $(IDL_GENERATED) $(HTML_EDITOR_GENERATED) $(error_DATA)
 CLEANFILES = $(BUILT_SOURCES)
 
 dist-hook:
Index: composer/e-msg-composer.c
===================================================================
RCS file: /cvs/gnome/evolution/composer/e-msg-composer.c,v
retrieving revision 1.459
diff -u -3 -r1.459 e-msg-composer.c
--- composer/e-msg-composer.c	13 Apr 2004 18:02:30 -0000	1.459
+++ composer/e-msg-composer.c	16 Apr 2004 09:11:32 -0000
@@ -91,6 +91,7 @@
 #include "e-util/e-signature-list.h"
 #include "widgets/misc/e-charset-picker.h"
 #include "widgets/misc/e-expander.h"
+#include "widgets/misc/e-error.h"
 
 #include <camel/camel-session.h>
 #include <camel/camel-charset-map.h>
@@ -1433,6 +1434,7 @@
 	closedir (dir);
 	
 	if (match != NULL) {
+#if 0
 		GtkWidget *dialog;
 
 		dialog = gtk_message_dialog_new (parent,
@@ -1443,6 +1445,9 @@
 		gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
 		load = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
 		gtk_widget_destroy (dialog);
+#else
+		load = e_error_run(parent, "mail-composer:recover-autosave", NULL) == GTK_RESPONSE_YES;
+#endif
 	}
 	
 	while (match != NULL) {
@@ -1598,7 +1603,8 @@
 	gdk_window_raise (GTK_WIDGET (composer)->window);
 	
 	subject = e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (composer->hdrs));
-	
+
+#if 0	
 	dialog = gtk_message_dialog_new (GTK_WINDOW (composer),
 					 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 					 GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
@@ -1615,7 +1621,10 @@
 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
 	button = gtk_dialog_run (GTK_DIALOG (dialog));
 	gtk_widget_destroy (dialog);
-	
+#else
+	button = e_error_run((GtkWindow *)composer, "mail-composer:exit-unsaved",
+			     subject && subject[0] ? subject : _("Untitled Message"));
+#endif
 	switch (button) {
 	case GTK_RESPONSE_YES:
 		/* Save */
Index: composer/mail-composer-errors.xml.in
===================================================================
RCS file: composer/mail-composer-errors.xml.in
diff -N composer/mail-composer-errors.xml.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ composer/mail-composer-errors.xml.in	16 Apr 2004 09:11:32 -0000
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<error-list domain="mail-composer">
+
+ <error id="attach" type="warning">
+  <_primary>You cannot attach the file `${0}' to this message.</_primary>
+  <_secondary>${0}</_secondary>
+ </error>
+
+ <error id="attach-notfile" type="warning">
+  <_primary>The file `${0}' is not a regular file and cannot be sent in a message.</_primary>
+ </error>
+
+ <error id="attach-directory">
+  <_primary>Directories can not be attached to Messages.</_primary>
+  <_secondary>To attach the contents of this directory, either attach the files in this directory individually, or create an archive of the directory and attach it.</_secondary>
+ </error>
+
+ <error id="recover-autosave" type="question">
+  <_title>Unfinished messages found</_title>
+  <_primary>Do you want to recover unfinished messages?</_primary>
+  <_secondary>Evolution quit unexpectedly while you were composing a new message. Recovering the message will allow you to continue where you left off.</_secondary>
+  <button _label="Don't Recover" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="Recover" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="exit-unsaved" modal="true" type="warning">
+  <_title>Warning: Modified Message</_title>
+  <_primary>Are you sure you want to discard the message, titled '{0}', you are composing?</_primary>
+  <_secondary>Closing this composer window will discard the message permanently, unless you choose to save the message in your Drafts folder. This will allow you to continue the message at a later date.</_secondary>
+  <button _label="Discard Changes" response="GTK_RESPONSE_NO"/>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="Save Message" response="GTK_RESPONSE_YES"/>
+ </error>
+
+</error-list>
Index: filter/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/filter/Makefile.am,v
retrieving revision 1.50
diff -u -3 -r1.50 Makefile.am
--- filter/Makefile.am	3 Feb 2004 17:32:18 -0000	1.50
+++ filter/Makefile.am	16 Apr 2004 09:11:32 -0000
@@ -70,6 +70,7 @@
 
 EXTRA_DIST =					\
 	$(glade_DATA)				\
+	$(error_in_files)			\
 	ChangeLog.pre-1-4			\
 	filter-marshal.list			\
 	filtertypes.xml				\
@@ -80,6 +81,11 @@
 # basic rules.
 filterdir = $(privdatadir)
 filter_DATA = filtertypes.xml vfoldertypes.xml searchtypes.xml
+
+error_in_files = filter-errors.xml.in
+error_DATA = $(error_in_files:.xml.in=.xml)
+errordir = $(privdatadir)/errors
+ INTLTOOL_XML_RULE@
 
 libfilter-i18n.h: filtertypes.xml vfoldertypes.xml
 	echo "/* Automatically generated. Do not edit. */" > $@;	      \
Index: filter/filter-datespec.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/filter-datespec.c,v
retrieving revision 1.35
diff -u -3 -r1.35 filter-datespec.c
--- filter/filter-datespec.c	20 Mar 2004 05:01:24 -0000	1.35
+++ filter/filter-datespec.c	16 Apr 2004 09:11:33 -0000
@@ -20,7 +20,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -36,6 +35,7 @@
 
 #include "filter-datespec.h"
 #include "e-util/e-sexp.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x)
 
@@ -173,7 +173,6 @@
 validate (FilterElement *fe)
 {
 	FilterDatespec *fds = (FilterDatespec *) fe;
-	GtkWidget *dialog;
 	gboolean valid;
 	
 	valid = fds->type != FDST_UNKNOWN;
@@ -182,13 +181,7 @@
                    GtkWidget member pointing to the value gotten with
                    ::get_widget() so that we can get the parent window
                    here. */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must choose a date."));
-		
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
+		e_error_run(NULL, "filter:no-date", NULL);
 	}
 	
 	return valid;
Index: filter/filter-errors.xml.in
===================================================================
RCS file: filter/filter-errors.xml.in
diff -N filter/filter-errors.xml.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ filter/filter-errors.xml.in	16 Apr 2004 09:11:33 -0000
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<error-list domain="filter">
+
+ <error id="no-date" type="error">
+  <_primary>Missing date.</_primary>
+  <_secondary>You must choose a date.</_secondary>
+ </error>
+
+ <error id="no-file" type="error">
+  <_primary>Missing file name.</_primary>
+  <_secondary>You must specify a file name.</_secondary>
+ </error>
+
+ <error id="bad-file" type="error">
+  <_primary>File &quot;{0}&quot; does not exist or is not a regular file.</_primary>
+  <_secondary>You must specify a file name.</_secondary>
+ </error>
+
+ <error id="no-folder" type="error">
+  <_primary>Missing folder.</_primary>
+  <_secondary>You must specify a folder.</_secondary>
+ </error>
+
+ <error id="bad-regexp" type="error">
+  <_primary>Bad regular expression &quot;{0}&quot;.</_primary>
+  <_secondary>Could not compile regular expression &quot;{1}&quot;.</_secondary>
+ </error>
+
+ <error id="no-name" type="error">
+  <_primary>Missing name.</_primary>
+  <_secondary>You must name this filter.</_secondary>
+ </error>
+
+ <error id="no-name-vfolder" type="error">
+  <_primary>Missing name.</_primary>
+  <_secondary>You must name this vFolder.</_secondary>
+ </error>
+
+ <error id="bad-name-notunique" type="error">
+  <_primary>Name &quot;{0}&quot; already used.</_primary>
+  <_secondary>Please choose another name.</_secondary>
+ </error>
+
+ <error id="vfolder-no-source" type="error">
+  <_primary>No sources selected.</_primary>
+  <_secondary>You must specify at least one folder as a source.
+Either by selecting the folders individually, and/or by selecting
+all local folders, all remote folders, or both.</_secondary>
+ </error>
+
+</error-list>
\ No newline at end of file
Index: filter/filter-file.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/filter-file.c,v
retrieving revision 1.18
diff -u -3 -r1.18 filter-file.c
--- filter/filter-file.c	15 Mar 2004 20:37:47 -0000	1.18
+++ filter/filter-file.c	16 Apr 2004 09:11:33 -0000
@@ -35,6 +35,7 @@
 
 #include "filter-file.h"
 #include "e-util/e-sexp.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x)
 
@@ -153,7 +154,6 @@
 validate (FilterElement *fe)
 {
 	FilterFile *file = (FilterFile *) fe;
-	GtkWidget *dialog;
 	struct stat st;
 	
 	if (!file->path) {
@@ -161,14 +161,8 @@
                    GtkWidget member pointing to the value gotten with
                    ::get_widget() so that we can get the parent window
                    here. */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must specify a file name."));
-		
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
-		
+		e_error_run(NULL, "filter:no-file", NULL);
+
 		return FALSE;
 	}
 	
@@ -180,14 +174,7 @@
 			   GtkWidget member pointing to the value gotten with
 			   ::get_widget() so that we can get the parent window
 			   here. */
-			dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-							 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							 _("File '%s' does not exist or is not a regular file."),
-							 file->path);
-			
-			gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
+			e_error_run(NULL, "filter:bad-file", file->path, NULL);
 			
 			return FALSE;
 		}
Index: filter/filter-folder.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/filter-folder.c,v
retrieving revision 1.38
diff -u -3 -r1.38 filter-folder.c
--- filter/filter-folder.c	15 Mar 2004 20:37:47 -0000	1.38
+++ filter/filter-folder.c	16 Apr 2004 09:11:33 -0000
@@ -34,6 +34,7 @@
 #include "mail/em-folder-selection-button.h"
 #include "mail/mail-component.h"
 #include "e-util/e-sexp.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x)
 
@@ -139,7 +140,6 @@
 validate (FilterElement *fe)
 {
 	FilterFolder *ff = (FilterFolder *) fe;
-	GtkWidget *dialog;
 	
 	if (ff->uri && *ff->uri) {
 		return TRUE;
@@ -148,14 +148,8 @@
                    GtkWidget member pointing to the value gotten with
                    ::get_widget() so that we can get the parent window
                    here. */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must specify a folder."));
-
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
-		
+		e_error_run(NULL, "filter:no-folder", NULL);
+
 		return FALSE;
 	}
 }
Index: filter/filter-input.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/filter-input.c,v
retrieving revision 1.29
diff -u -3 -r1.29 filter-input.c
--- filter/filter-input.c	15 Mar 2004 20:37:47 -0000	1.29
+++ filter/filter-input.c	16 Apr 2004 09:11:34 -0000
@@ -34,6 +34,7 @@
 
 #include "filter-input.h"
 #include "e-util/e-sexp.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x)
 
@@ -165,7 +166,6 @@
 {
 	FilterInput *fi = (FilterInput *)fe;
 	gboolean valid = TRUE;
-	GtkWidget *dialog;
 	
 	if (fi->values && !strcmp (fi->type, "regex")) {
 		const char *pattern;
@@ -188,14 +188,7 @@
 			   GtkWidget member pointing to the value gotten with
 			   ::get_widget() so that we can get the parent window
 			   here. */
-			dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-							 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							 _("Error in regular expression '%s':\n%s"),
-							 pattern, regmsg);
-			
-			gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
+			e_error_run(NULL, "filter:bad-regexp", pattern, regmsg, NULL);
 			g_free (regmsg);
 			
 			valid = FALSE;
Index: filter/filter-rule.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/filter-rule.c,v
retrieving revision 1.61
diff -u -3 -r1.61 filter-rule.c
--- filter/filter-rule.c	29 Mar 2004 22:25:15 -0000	1.61
+++ filter/filter-rule.c	16 Apr 2004 09:11:35 -0000
@@ -30,6 +30,7 @@
 #include <gtk/gtk.h>
 #include <libgnome/gnome-i18n.h>
 
+#include "widgets/misc/e-error.h"
 #include "filter-rule.h"
 #include "filter-context.h"
 #include "filter-marshal.h"
@@ -205,7 +206,6 @@
 static int
 validate (FilterRule *fr)
 {
-	GtkWidget *dialog;
 	int valid = TRUE;
 	GList *parts;
 	
@@ -214,13 +214,7 @@
                    GtkWidget member pointing to the value gotten with
                    ::get_widget() so that we can get the parent window
                    here. */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must name this filter."));
-		
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
+		e_error_run(NULL, "filter:no-name", NULL);
 		
 		return FALSE;
 	}
Index: filter/rule-context.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/rule-context.c,v
retrieving revision 1.54
diff -u -3 -r1.54 rule-context.c
--- filter/rule-context.c	15 Mar 2004 20:37:48 -0000	1.54
+++ filter/rule-context.c	16 Apr 2004 09:11:35 -0000
@@ -35,6 +35,7 @@
 #include <gtk/gtk.h>
 #include <libgnome/gnome-i18n.h>
 #include <gal/util/e-xml-utils.h>
+#include "widgets/misc/e-error.h"
 
 #include "rule-context.h"
 #include "filter-rule.h"
@@ -677,14 +678,7 @@
 		}
 
 		if (rule_context_find_rule (context, rule->name, rule->source)) {
-			dialog = gtk_message_dialog_new ((GtkWindow *) dialog, GTK_DIALOG_DESTROY_WITH_PARENT,
-							 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							 _("Rule name '%s' is not unique, choose another."),
-							 rule->name);
-
-			gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
+			e_error_run((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
 
 			return;
 		}
Index: filter/rule-editor.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/rule-editor.c,v
retrieving revision 1.60
diff -u -3 -r1.60 rule-editor.c
--- filter/rule-editor.c	31 Mar 2004 10:05:34 -0000	1.60
+++ filter/rule-editor.c	16 Apr 2004 09:11:36 -0000
@@ -27,9 +27,11 @@
 
 /* for getenv only, remove when getenv need removed */
 #include <stdlib.h>
+#include <string.h>
 
 #include <libgnome/gnome-i18n.h>
 
+#include "widgets/misc/e-error.h"
 #include "rule-editor.h"
 
 static int enable_undo = 0;
@@ -234,15 +236,7 @@
 		}
 		
 		if (rule_context_find_rule (re->context, re->edit->name, re->edit->source)) {
-			dialog = gtk_message_dialog_new ((GtkWindow *) dialog, GTK_DIALOG_DESTROY_WITH_PARENT,
-							 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							 _("Rule name '%s' is not unique, choose another."),
-							 re->edit->name);
-			
-			gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
-			
+			e_error_run((GtkWindow *)dialog, "filter:bad-name-notunique", re->edit->name, NULL);
 			return;
 		}
 		
@@ -320,15 +314,7 @@
 		
 		rule = rule_context_find_rule (re->context, re->edit->name, re->edit->source);
 		if (rule != NULL && rule != re->current) {
-			dialog = gtk_message_dialog_new ((GtkWindow *) dialog,
-							 GTK_DIALOG_DESTROY_WITH_PARENT,
-							 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							 _("Rule name '%s' is not unique, choose another."),
-							 re->edit->name);
-			
-			gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
+			e_error_run((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
 			
 			return;
 		}
Index: filter/vfolder-rule.c
===================================================================
RCS file: /cvs/gnome/evolution/filter/vfolder-rule.c,v
retrieving revision 1.44
diff -u -3 -r1.44 vfolder-rule.c
--- filter/vfolder-rule.c	15 Mar 2004 20:37:48 -0000	1.44
+++ filter/vfolder-rule.c	16 Apr 2004 09:11:37 -0000
@@ -37,6 +37,7 @@
 #include "mail/em-folder-tree.h"
 #include "mail/em-folder-selector.h"
 #include "mail/mail-component.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x) 
 
@@ -204,20 +205,11 @@
 static int
 validate (FilterRule *fr)
 {
-	GtkWidget *dialog;
-	
 	g_return_val_if_fail (fr != NULL, FALSE);
 	
 	if (!fr->name || !*fr->name) {
 		/* FIXME: set a aprent window? */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must name this vfolder."));
-		
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
-		
+		e_error_run(NULL, "filter:no-name-vfolder", NULL);
 		return 0;
 	}
 	
@@ -225,14 +217,7 @@
 	   Do not translate this string! */
 	if (((VfolderRule *)fr)->with == VFOLDER_RULE_WITH_SPECIFIC && ((VfolderRule *)fr)->sources == NULL) {
 		/* FIXME: set a parent window? */
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You need to to specify at least one folder as a source."));
-		
-		gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
-		
+		e_error_run(NULL, "filter:vfolder-no-source", NULL);
 		return 0;
 	}
 	
Index: mail/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/mail/Makefile.am,v
retrieving revision 1.243
diff -u -3 -r1.243 Makefile.am
--- mail/Makefile.am	31 Mar 2004 10:20:56 -0000	1.243
+++ mail/Makefile.am	16 Apr 2004 09:11:38 -0000
@@ -184,6 +184,11 @@
 
 # Misc data to install
 
+error_in_files = mail-errors.xml.in
+error_DATA = $(error_in_files:.xml.in=.xml)
+errordir = $(privdatadir)/errors
+ INTLTOOL_XML_RULE@
+
 glade_DATA = mail-config.glade  subscribe-dialog.glade message-tags.glade mail-search.glade mail-security.glade
 MARSHAL_GENERATED = em-marshal.c em-marshal.h
 @EVO_MARSHAL_RULE@
@@ -194,6 +199,7 @@
 	ChangeLog.pre-1-4		\
 	em-marshal.list			\
 	$(SPELL_IDL)			\
+	$(error_in_files)		\
 	$(glade_DATA)			\
 	$(schema_DATA)			\
 	$(server_in_files)		\
@@ -237,5 +243,6 @@
 dist-hook:
 	cd $(distdir); rm -f $(BUILT_SOURCES)
 
-BUILT_SOURCES = $(SPELL_IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA)
+BUILT_SOURCES = $(SPELL_IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA) $(error_DATA)
+
 CLEANFILES = $(BUILT_SOURCES)
Index: mail/em-account-prefs.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-account-prefs.c,v
retrieving revision 1.6
diff -u -3 -r1.6 em-account-prefs.c
--- mail/em-account-prefs.c	7 Apr 2004 20:52:49 -0000	1.6
+++ mail/em-account-prefs.c	16 Apr 2004 09:11:39 -0000
@@ -37,6 +37,7 @@
 #include "mail-send-recv.h"
 
 #include "e-util/e-account-list.h"
+#include "widgets/misc/e-error.h"
 
 #include "art/mark.xpm"
 
@@ -214,7 +215,6 @@
 	EAccount *account = NULL;
 	EAccountList *accounts;
 	GtkTreeModel *model;
-	GtkWidget *confirm;
 	GtkTreeIter iter;
 	int ans;
 	
@@ -226,24 +226,7 @@
 	if (account == NULL || prefs->editor != NULL)
 		return;
 	
-	confirm = gtk_message_dialog_new (PREFS_WINDOW (prefs),
-					  GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
-					  GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
-					  _("Are you sure you want to delete this account?"));
-	
-	button = (GtkButton *) gtk_button_new_from_stock (GTK_STOCK_YES);
-	gtk_button_set_label (button, _("Delete"));
-	gtk_dialog_add_action_widget ((GtkDialog *) confirm, (GtkWidget *) button, GTK_RESPONSE_YES);
-	gtk_widget_show ((GtkWidget *) button);
-	
-	button = (GtkButton *) gtk_button_new_from_stock (GTK_STOCK_NO);
-	gtk_button_set_label (button, _("Don't delete"));
-	gtk_dialog_add_action_widget ((GtkDialog *) confirm, (GtkWidget *) button, GTK_RESPONSE_NO);
-	gtk_widget_show ((GtkWidget *) button);
-	
-	ans = gtk_dialog_run ((GtkDialog *) confirm);
-	gtk_widget_destroy (confirm);
-	
+	ans = e_error_run(PREFS_WINDOW(prefs), "mail:ask-account-delete", NULL);
 	if (ans == GTK_RESPONSE_YES) {
 		int len;
 		
Index: mail/em-composer-prefs.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-composer-prefs.c,v
retrieving revision 1.10
diff -u -3 -r1.10 em-composer-prefs.c
--- mail/em-composer-prefs.c	7 Apr 2004 20:52:49 -0000	1.10
+++ mail/em-composer-prefs.c	16 Apr 2004 09:11:40 -0000
@@ -47,6 +47,7 @@
 #include <gtk/gtktreeview.h>
 
 #include "widgets/misc/e-charset-picker.h"
+#include "widgets/misc/e-error.h"
 
 #include "mail-config.h"
 
@@ -360,7 +361,6 @@
 {
 	const char *name;
 	char *script;
-	GtkWidget *dialog;
 	GtkWidget *entry;
 	
 	if (button == GTK_RESPONSE_ACCEPT) {
@@ -395,14 +395,8 @@
 			}
 		}
 		
+		e_error_run((GtkWindow *)prefs->sig_script_dialog, "mail:signature-notscript", script, NULL);
 		g_free(script);
-		dialog = gtk_message_dialog_new (GTK_WINDOW (prefs->sig_script_dialog),
-						 GTK_DIALOG_DESTROY_WITH_PARENT,
-						 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-						 "%s", _("You must specify a valid script name."));
-		
-		gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
 		return;
 	}
 	
Index: mail/em-composer-utils.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-composer-utils.c,v
retrieving revision 1.9
diff -u -3 -r1.9 em-composer-utils.c
--- mail/em-composer-utils.c	9 Apr 2004 17:30:58 -0000	1.9
+++ mail/em-composer-utils.c	16 Apr 2004 09:11:40 -0000
@@ -20,13 +20,14 @@
  *
  */
 
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <string.h>
 
+#include <gtk/gtkdialog.h>
+
 #include "mail-mt.h"
 #include "mail-ops.h"
 #include "mail-tools.h"
@@ -35,7 +36,7 @@
 #include "mail-send-recv.h"
 #include "mail-component.h"
 
-#include <e-util/e-dialog-utils.h>  /* e_notice */
+#include "widgets/misc/e-error.h"
 
 #include "em-utils.h"
 #include "em-composer-utils.h"
@@ -119,48 +120,31 @@
 		}
 	}
 
-	/* FIXME: this wording sucks */
-	res = em_utils_prompt_user((GtkWindow *) composer, GTK_RESPONSE_YES, "/apps/evolution/mail/prompts/unwanted_html",
-				   _("You are sending an HTML-formatted message. Please make sure that\n"
-				     "the following recipients are willing and able to receive HTML mail:\n"
-				     "%s"
-				     "Send anyway?"),
-				   str->str);
-	g_string_free (str, TRUE);
-	
+	res = em_utils_prompt_user((GtkWindow *)composer,"/apps/evolution/mail/prompts/unwanted_html",
+				   "mail:ask-send-html", str->str, NULL);
+	g_string_free(str, TRUE);
+
 	return res;
 }
 
 static gboolean
 ask_confirm_for_empty_subject (EMsgComposer *composer)
 {
-	return em_utils_prompt_user((GtkWindow *)composer, GTK_RESPONSE_YES, "/apps/evolution/mail/prompts/empty_subject",
-				    _("This message has no subject.\nReally send?"));
+	return em_utils_prompt_user((GtkWindow *)composer, "/apps/evolution/mail/prompts/empty_subject",
+				    "mail:ask-send-no-subject", NULL);
 }
 
 static gboolean
 ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case)
 {
-	const char *first_text;
-	
 	/* If the user is mailing a hidden contact list, it is possible for
 	   them to create a message with only Bcc recipients without really
 	   realizing it.  To try to avoid being totally confusing, I've changed
 	   this dialog to provide slightly different text in that case, to
 	   better explain what the hell is going on. */
 	
-	if (hidden_list_case) {
-		first_text =  _("Since the contact list you are sending to "
-				"is configured to hide the list's addresses, "
-				"this message will contain only Bcc recipients.");
-	} else {
-		first_text = _("This message contains only Bcc recipients.");
-	}
-
-	return em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, "/apps/evolution/mail/prompts/only_bcc",
-				     "%s\n%s", first_text,
-				     _("It is possible that the mail server may reveal the recipients "
-				       "by adding an Apparently-To header.\nSend anyway?"));
+	return em_utils_prompt_user((GtkWindow *)composer, "/apps/evolution/mail/prompts/only_bcc",
+				    hidden_list_case?"mail:ask-send-only-bcc-contact":"mail:ask-send-only-bcc", NULL);
 }
 
 struct _send_data {
@@ -301,8 +285,7 @@
 			if (no_recipients)
 				*no_recipients = TRUE;
 		} else {
-			e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
-				  _("You must specify recipients in order to send this message."));
+			e_error_run((GtkWindow *)composer, "mail:send-no-recipients", NULL);
 			goto finished;
 		}
 	}
@@ -570,9 +553,7 @@
 		mail_msg_wait (id);
 		
 		if (!folder) {
-			if (!em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL,
-						   _("Unable to open the drafts folder for this account.\n"
-						     "Would you like to use the default drafts folder?")))
+			if (e_error_run((GtkWindow *)composer, "mail:ask-default-drafts", NULL) != GTK_RESPONSE_YES)
 				return;
 			
 			folder = drafts_folder;
Index: mail/em-folder-tree.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-folder-tree.c,v
retrieving revision 1.90
diff -u -3 -r1.90 em-folder-tree.c
--- mail/em-folder-tree.c	12 Apr 2004 19:11:14 -0000	1.90
+++ mail/em-folder-tree.c	16 Apr 2004 09:11:43 -0000
@@ -49,7 +49,8 @@
 
 #include "e-util/e-mktemp.h"
 #include "e-util/e-request.h"
-#include "e-util/e-dialog-utils.h"
+
+#include "widgets/misc/e-error.h"
 
 #include "filter/vfolder-rule.h"
 
@@ -1971,11 +1972,9 @@
 {
 	struct _copy_folder_data *cfd = data;
 	struct _EMFolderTreePrivate *priv;
-	CamelStore *fromstore, *tostore;
+	CamelStore *fromstore = NULL, *tostore = NULL;
 	char *tobase = NULL, *frombase;
-	GtkWindow *parent;
 	CamelException ex;
-	GtkWidget *dialog;
 	CamelURL *url;
 
 	if (uri == NULL) {
@@ -1988,20 +1987,24 @@
 	d(printf ("%sing folder '%s' to '%s'\n", cfd->delete ? "move" : "copy", priv->selected_path, uri));
 	
 	camel_exception_init (&ex);
-	if (!(fromstore = camel_session_get_store (session, priv->selected_uri, &ex)))
-		goto exception;
-	
 	frombase = priv->selected_path + 1;
-	if (fromstore == mail_component_peek_local_store (NULL) && is_special_local_folder (frombase)) {
-		if (cfd->delete)
-			camel_exception_setv (&ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot move folder `%s': illegal operation"), frombase);
-		camel_object_unref (fromstore);
-		goto exception;
+
+	if (!(fromstore = camel_session_get_store (session, priv->selected_uri, &ex))) {
+		e_error_run((GtkWindow *)gtk_widget_get_ancestor ((GtkWidget *) cfd->emft, GTK_TYPE_WINDOW),
+			    cfd->delete?"mail:no-move-folder-notexist":"mail:no-copy-folder-notexist", frombase, uri, ex.desc, NULL);
+		goto fail;
+	}
+	
+	if (cfd->delete && fromstore == mail_component_peek_local_store (NULL) && is_special_local_folder (frombase)) {
+		e_error_run((GtkWindow *)gtk_widget_get_ancestor ((GtkWidget *) cfd->emft, GTK_TYPE_WINDOW),
+			    "mail:no-rename-special-folder", frombase, NULL);
+		goto fail;
 	}
 	
 	if (!(tostore = camel_session_get_store (session, uri, &ex))) {
-		camel_object_unref (fromstore);
-		goto exception;
+		e_error_run((GtkWindow *)gtk_widget_get_ancestor ((GtkWidget *) cfd->emft, GTK_TYPE_WINDOW),
+			    cfd->delete?"mail:no-move-folder-to-notexist":"mail:no-move-folder-to-notexist", frombase, uri, ex.desc, NULL);
+		goto fail;
 	}
 	
 	url = camel_url_new (uri, NULL);
@@ -2015,18 +2018,12 @@
 	emft_copy_folders (tostore, tobase, fromstore, frombase, cfd->delete);
 	
 	camel_url_free (url);
-	g_free (cfd);
-	
-	return;
-	
- exception:
-	
-	parent = (GtkWindow *) gtk_widget_get_ancestor ((GtkWidget *) cfd->emft, GTK_TYPE_WINDOW);
-	dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-					 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("%s"), ex.desc);
-	g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
+fail:
+	if (fromstore)
+		camel_object_unref(fromstore);
+	if (tostore)
+		camel_object_unref(tostore);
 	camel_exception_clear (&ex);
-	gtk_widget_show (dialog);
 	g_free (cfd);
 }
 
@@ -2173,40 +2170,31 @@
 	struct _EMFolderTreePrivate *priv = emft->priv;
 	struct _EMFolderTreeModelStoreInfo *si;
 	gboolean created = FALSE;
-	GtkWindow *window;
-	GtkWidget *dialog;
 	CamelStore *store;
 	CamelException ex;
 	
 	d(printf ("Creating folder: %s (%s)\n", path, uri));
 	
 	camel_exception_init (&ex);
-	if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex)))
-		goto exception;
+	if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) {
+		e_error_run((GtkWindow *)gtk_widget_get_ancestor((GtkWidget *)emft, GTK_TYPE_WINDOW),
+			    "mail:no-create-folder-nostore", path, ex.desc, NULL);
+		goto fail;
+	}
 	
 	if (!(si = g_hash_table_lookup (priv->model->store_hash, store))) {
 		abort();
 		camel_object_unref (store);
-		goto exception;
+		goto fail;
 	}
 	
 	camel_object_unref (store);
 	
 	mail_msg_wait (emft_create_folder (si->store, path, created_cb, &created));
+fail:
+	camel_exception_clear(&ex);
 	
 	return created;
-	
- exception:
-	
-	window = (GtkWindow *) gtk_widget_get_ancestor ((GtkWidget *) emft, GTK_TYPE_WINDOW);
-	dialog = gtk_message_dialog_new (window, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-					 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("%s"), ex.desc);
-	g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
-	camel_exception_clear (&ex);
-	
-	gtk_widget_show (dialog);
-	
-	return FALSE;
 }
 
 static void
@@ -2393,7 +2381,7 @@
 	camel_exception_init (&ex);
 	emft_popup_delete_folders (store, path, &ex);
 	if (camel_exception_is_set (&ex)) {
-		e_notice (NULL, GTK_MESSAGE_ERROR, _("Could not delete folder: %s"), ex.desc);
+		e_error_run(NULL, "mail:no-delete-folder", path, ex.desc, NULL);
 		camel_exception_clear (&ex);
 	}
 }
@@ -2408,7 +2396,7 @@
 	GtkTreeIter iter;
 	GtkWidget *dialog;
 	const char *full_name;
-	char *title, *path;
+	char *path;
 	
 	selection = gtk_tree_view_get_selection (priv->treeview);
 	if (!emft_selection_get_selected (selection, &model, &iter))
@@ -2420,26 +2408,11 @@
 	
 	full_name = path[0] == '/' ? path + 1 : path;
 	if (store == local && is_special_local_folder (full_name)) {
-		e_notice (NULL, GTK_MESSAGE_ERROR, _("Cannot delete local %s folder."), full_name);
+		e_error_run(NULL, "mail:no-delete-spethal-folder", full_name, NULL);
 		return;
 	}
-	
-	dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-					 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
-					 _("Really delete folder \"%s\" and all of its subfolders?"),
-					 full_name);
-	
-	gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
-	gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_DELETE, GTK_RESPONSE_OK);
-	
-	gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_OK);
-	gtk_container_set_border_width ((GtkContainer *) dialog, 6); 
-	gtk_box_set_spacing ((GtkBox *) ((GtkDialog *) dialog)->vbox, 6);
-	
-	title = g_strdup_printf (_("Delete \"%s\""), full_name);
-	gtk_window_set_title ((GtkWindow *) dialog, title);
-	g_free (title);
-	
+
+	dialog = e_error_new(NULL, "mail:ask-delete-folder", full_name, NULL);
 	g_signal_connect (dialog, "response", G_CALLBACK (emft_popup_delete_response), emft);
 	gtk_widget_show (dialog);
 }
@@ -2472,7 +2445,7 @@
 	
 	/* don't allow user to rename one of the special local folders */
 	if (store == local && is_special_local_folder (full_name)) {
-		e_notice (NULL, GTK_MESSAGE_ERROR, _("Cannot rename local %s folder."), full_name);
+		e_error_run(NULL, "mail:no-rename-spethal-folder", full_name, NULL);
 		return;
 	}
 	
@@ -2505,10 +2478,7 @@
 			camel_exception_init (&ex);
 			if ((fi = camel_store_get_folder_info (store, path, CAMEL_STORE_FOLDER_INFO_FAST, &ex)) != NULL) {
 				camel_store_free_folder_info (store, fi);
-				
-				e_notice (NULL, GTK_MESSAGE_ERROR,
-					  _("A folder named \"%s\" already exists. Please use a different name."),
-					  new_name);
+				e_error_run(NULL, "mail:no-rename-folder-exists", name, new_name, NULL);
 			} else {
 				const char *oldpath, *newpath;
 				
@@ -2520,7 +2490,7 @@
 				camel_exception_clear (&ex);
 				camel_store_rename_folder (store, oldpath, newpath, &ex);
 				if (camel_exception_is_set (&ex)) {
-					e_notice (NULL, GTK_MESSAGE_ERROR, _("Could not rename folder: %s"), ex.desc);
+					e_error_run(NULL, "mail:no-rename-folder", oldpath, newpath, ex.desc, NULL);
 					camel_exception_clear (&ex);
 				}
 				
Index: mail/em-utils.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-utils.c,v
retrieving revision 1.30
diff -u -3 -r1.30 em-utils.c
--- mail/em-utils.c	13 Apr 2004 07:50:59 -0000	1.30
+++ mail/em-utils.c	16 Apr 2004 09:11:46 -0000
@@ -46,8 +46,9 @@
 #include "message-tag-followup.h"
 
 #include <e-util/e-mktemp.h>
-#include <e-util/e-dialog-utils.h>
 #include <e-util/e-account-list.h>
+#include <e-util/e-dialog-utils.h>
+#include "widgets/misc/e-error.h"
 
 #include <gal/util/e-util.h>
 
@@ -63,10 +64,9 @@
 /**
  * em_utils_prompt_user:
  * @parent: parent window
- * @def: default response
  * @promptkey: gconf key to check if we should prompt the user or not.
- * @fmt: prompt format
- * @Varargs: varargs
+ * @tag: e_error tag.
+ * @arg0: The first of a NULL terminated list of arguments for the error.
  *
  * Convenience function to query the user with a Yes/No dialog and a
  * "Don't show this dialog again" checkbox. If the user checks that
@@ -76,29 +76,25 @@
  * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
  **/
 gboolean
-em_utils_prompt_user(GtkWindow *parent, int def, const char *promptkey, const char *fmt, ...)
+em_utils_prompt_user(GtkWindow *parent, const char *promptkey, const char *tag, const char *arg0, ...)
 {
 	GtkWidget *mbox, *check = NULL;
 	va_list ap;
 	int button;
-	char *str;
 	GConfClient *gconf = mail_config_get_gconf_client();
 
 	if (promptkey
 	    && !gconf_client_get_bool(gconf, promptkey, NULL))
 		return TRUE;
-	
-	va_start (ap, fmt);
-	str = g_strdup_vprintf (fmt, ap);
-	va_end (ap);
-	mbox = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
-				       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
-				       "%s", str);
-	g_free (str);
-	gtk_dialog_set_default_response ((GtkDialog *) mbox, def);
+
+	va_start(ap, arg0);
+	mbox = e_error_newv(parent, tag, arg0, ap);
+	va_end(ap);
+
 	if (promptkey) {
 		check = gtk_check_button_new_with_label (_("Don't show this message again."));
-		gtk_box_pack_start ((GtkBox *)((GtkDialog *) mbox)->vbox, check, TRUE, TRUE, 10);
+		gtk_container_set_border_width((GtkContainer *)check, 12);
+		gtk_box_pack_start ((GtkBox *)((GtkDialog *) mbox)->vbox, check, TRUE, TRUE, 0);
 		gtk_widget_show (check);
 	}
 	
@@ -278,9 +274,7 @@
 	g_free (user);
 	
 	if (((RuleContext *) fc)->error) {
-		e_notice (parent, GTK_MESSAGE_ERROR,
-			  _("Error loading filter information:\n%s"),
-			  ((RuleContext *) fc)->error);
+		e_error_run((GtkWindow *)parent, "mail:filter-load-error", ((RuleContext *)fc)->error, NULL);
 		return;
 	}
 	
@@ -1432,13 +1426,11 @@
 	
 	if (access (path, F_OK) == 0) {
 		if (access (path, W_OK) != 0) {
-			e_notice (parent, GTK_MESSAGE_ERROR,
-				 _("Cannot save to `%s'\n %s"), path, g_strerror (errno));
+			e_error_run(parent, "mail:no-save-path", path, g_strerror(errno), NULL);
 			return FALSE;
 		}
 		
-		return em_utils_prompt_user (parent, GTK_RESPONSE_NO, NULL,
-					     _("`%s' already exists.\nOverwrite it?"), path);
+		return e_error_run(parent, "mail:ask-save-path-overwrite", path, NULL) == GTK_RESPONSE_OK;
 	}
 	
 	return TRUE;
@@ -1516,8 +1508,7 @@
 	
 	dirname = g_path_get_dirname(filename);
 	if (camel_mkdir(dirname, 0777) == -1) {
-		e_notice(parent, GTK_MESSAGE_ERROR,
-				 _("Cannot save to `%s'\n %s"), filename, g_strerror(errno));
+		e_error_run((GtkWindow *)parent, "mail:no-create-path", filename, g_strerror(errno), NULL);
 		g_free(dirname);
 		return FALSE;
 	}
@@ -1525,15 +1516,13 @@
 
 	if (access(filename, F_OK) == 0) {
 		if (access(filename, W_OK) != 0) {
-			e_notice(parent, GTK_MESSAGE_ERROR,
-				 _("Cannot save to `%s'\n %s"), filename, g_strerror(errno));
+			e_error_run((GtkWindow *)parent, "mail:no-write-path-exists", filename, NULL);
 			return FALSE;
 		}
 	}
 	
 	if (stat(filename, &st) != -1 && !S_ISREG(st.st_mode)) {
-		e_notice(parent, GTK_MESSAGE_ERROR,
-				 _("Error: '%s' exists and is not a regular file"), filename);
+		e_error_run((GtkWindow *)parent, "no-write-path-notfile", filename, NULL);
 		return FALSE;
 	}
 	
@@ -2121,10 +2110,7 @@
 
 	tmpdir = e_mkdtemp("evolution-tmp-XXXXXX");
 	if (tmpdir == NULL) {
-		e_notice(parent, GTK_MESSAGE_ERROR,
-			 _("Could not create temporary directory: %s"),
-			 g_strerror (errno));
-
+		e_error_run((GtkWindow *)parent, "mail:no-create-tmp-path", g_strerror(errno), NULL);
 		return NULL;
 	}
 
@@ -2451,12 +2437,7 @@
 
 	camel_object_get(folder, NULL, CAMEL_OBJECT_DESCRIPTION, &name, 0);
 
-	if (!em_utils_prompt_user ((GtkWindow *) parent, GTK_RESPONSE_NO, 
-				   "/apps/evolution/mail/prompts/expunge",
-				   _("This operation will permanently remove all deleted messages "
-				     "in the folder `%s'. If you continue, you "
-				     "will not be able to recover these messages.\n"
-				     "\nReally erase these messages?"), name))
+	if (!em_utils_prompt_user ((GtkWindow *) parent, "/apps/evolution/mail/prompts/expunge", "mail:ask-expunge", name, NULL))
 		return;
 	
 	mail_expunge_folder(folder, NULL, NULL);
@@ -2477,11 +2458,7 @@
 	EIterator *iter;
 	CamelException ex;
 	
-	if (!em_utils_prompt_user ((GtkWindow *) parent, GTK_RESPONSE_NO, "/apps/evolution/mail/prompts/empty_trash",
-				   _("This operation will permanently remove all deleted messages "
-				     "in all folders. If you continue, you will not be able to "
-				     "recover these messages.\n"
-				     "\nReally erase these messages?")))
+	if (!em_utils_prompt_user((GtkWindow *) parent, "/apps/evolution/mail/prompts/empty_trash", "mail:ask-empty-trash", NULL))
 		return;
 	
 	camel_exception_init (&ex);
Index: mail/em-utils.h
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-utils.h,v
retrieving revision 1.10
diff -u -3 -r1.10 em-utils.h
--- mail/em-utils.h	31 Mar 2004 10:09:04 -0000	1.10
+++ mail/em-utils.h	16 Apr 2004 09:11:46 -0000
@@ -42,7 +42,7 @@
 struct _GtkAdjustment;
 struct _EMsgComposer;
 
-gboolean em_utils_prompt_user(struct _GtkWindow *parent, int def, const char *promptkey, const char *fmt, ...);
+gboolean em_utils_prompt_user(struct _GtkWindow *parent, const char *promptkey, const char *tag, const char *arg0, ...);
 
 GPtrArray *em_utils_uids_copy (GPtrArray *uids);
 void em_utils_uids_free (GPtrArray *uids);
Index: mail/mail-account-editor.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-account-editor.c,v
retrieving revision 1.64
diff -u -3 -r1.64 mail-account-editor.c
--- mail/mail-account-editor.c	31 Mar 2004 10:09:04 -0000	1.64
+++ mail/mail-account-editor.c	16 Apr 2004 09:11:47 -0000
@@ -107,7 +107,7 @@
 	if (page != -1) {
 		gtk_notebook_set_current_page (editor->notebook, page);
 		gtk_widget_grab_focus (incomplete);
-		e_notice (editor, GTK_MESSAGE_ERROR, _("You have not filled in all of the required information."));
+		e_error_run(editor, "mail:account-incomplete", NULL);
 		return FALSE;
 	}
 	
Index: mail/mail-account-gui.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-account-gui.c,v
retrieving revision 1.158
diff -u -3 -r1.158 mail-account-gui.c
--- mail/mail-account-gui.c	8 Apr 2004 22:02:36 -0000	1.158
+++ mail/mail-account-gui.c	16 Apr 2004 09:11:49 -0000
@@ -22,7 +22,6 @@
  *
  */
 
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -34,9 +33,10 @@
 #include <gconf/gconf-client.h>
 
 #include <e-util/e-account-list.h>
-#include <e-util/e-dialog-utils.h>
 #include <e-util/e-signature-list.h>
 
+#include <widgets/misc/e-error.h>
+
 #include "em-account-prefs.h"
 #include "em-folder-selection-button.h"
 #include "mail-account-gui.h"
@@ -2116,7 +2116,7 @@
 	account = mail_config_get_account_by_name (new_name);
 	
 	if (account && account != new) {
-		e_notice (gui->account_name, GTK_MESSAGE_ERROR, _("You may not create two accounts with the same name."));
+		e_error_run(gui->account_name, "mail:account-notunique", NULL);
 		return FALSE;
 	}
 	
Index: mail/mail-autofilter.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-autofilter.c,v
retrieving revision 1.47
diff -u -3 -r1.47 mail-autofilter.c
--- mail/mail-autofilter.c	31 Mar 2004 10:09:04 -0000	1.47
+++ mail/mail-autofilter.c	16 Apr 2004 09:11:50 -0000
@@ -427,22 +427,17 @@
 		GString *s;
 		GList *l;
 		
-		s = g_string_new (_("The following filter rule(s):\n"));
+		s = g_string_new("");
 		l = deleted;
 		while (l) {
 			g_string_append_printf (s, "    %s\n", (char *)l->data);
 			l = l->next;
 		}
-		g_string_append_printf (s, _("Used the removed folder:\n    '%s'\n"
-					     "And have been updated."), euri);
-		
-		dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO,
-						 GTK_BUTTONS_CLOSE, "%s", s->str);
+
+		dialog = e_error_new(NULL, "mail:filter-updated", s->str, euri, NULL);
 		g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
-		
-		g_string_free (s, TRUE);
-		
-		gtk_widget_show (dialog);
+		g_string_free(s, TRUE);
+		gtk_widget_show(dialog);
 		
 		printf("Folder deleterename '%s' changed filters, resaving\n", euri);
 		if (rule_context_save ((RuleContext *) fc, user) == -1)
Index: mail/mail-component.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-component.c,v
retrieving revision 1.72
diff -u -3 -r1.72 mail-component.c
--- mail/mail-component.c	14 Apr 2004 09:48:36 -0000	1.72
+++ mail/mail-component.c	16 Apr 2004 09:11:51 -0000
@@ -45,6 +45,7 @@
 #include "em-migrate.h"
 
 #include "widgets/misc/e-info-label.h"
+#include "widgets/misc/e-error.h"
 
 #include "filter/rule-context.h"
 #include "mail-config.h"
@@ -68,7 +69,6 @@
 #include <gtk/gtklabel.h>
 
 #include <e-util/e-mktemp.h>
-#include <e-util/e-dialog-utils.h>
 
 #include <gal/e-table/e-tree.h>
 #include <gal/e-table/e-tree-memory.h>
@@ -572,20 +572,9 @@
 	folder = mc_default_folders[MAIL_COMPONENT_FOLDER_OUTBOX].folder;
 	if (folder != NULL
 	    && camel_folder_get_message_count(folder) != 0
-	    && camel_session_is_online(session)) {
-		GtkWidget *dialog;
-		guint resp;
-
-		/* FIXME: HIG? */
-		dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_YES_NO,
-						_("You have unsent messages, do you wish to quit anyway?"));
-		gtk_dialog_set_default_response((GtkDialog *)dialog, GTK_RESPONSE_NO);
-		resp = gtk_dialog_run((GtkDialog *)dialog);
-		gtk_widget_destroy(dialog);
-
-		if (resp != GTK_RESPONSE_YES)
-			return FALSE;
-	}
+	    && camel_session_is_online(session)
+	    && e_error_run(NULL, "mail:exit-unsaved", NULL) != GTK_RESPONSE_YES)
+		return FALSE;
 
 	return TRUE;
 }
Index: mail/mail-errors.xml.in
===================================================================
RCS file: mail/mail-errors.xml.in
diff -N mail/mail-errors.xml.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mail/mail-errors.xml.in	16 Apr 2004 09:11:52 -0000
@@ -0,0 +1,293 @@
+<?xml version="1.0"?>
+<error-list domain="mail">
+
+ <error id="camel-service-auth-invalid" type="warning">
+  <_primary>Invalid authentication</_primary>
+  <_secondary>This server does not support this type of authentication and may not support authentication at all.</_secondary>
+ </error>
+
+ <error id="camel-service-auth-failed" type="warning">
+  <_primary>Your login to your server &quot;{0}&quot; as &quot;{0}&quot; failed.</_primary>
+  <_secondary>Check to make sure your password is spelled correctly.  Remember that many passwords are case sensitive; your caps lock might be on.</_secondary>
+ </error>
+
+ <error id="ask-send-html" type="question" default="GTK_RESPONSE_YES">
+  <_primary>Are you sure you want to send a message in HTML format?</_primary>
+  <_secondary>Please make sure the following recipients are willing and able to receive HTML email:
+{0}
+Send anyway?</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Send" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="ask-send-no-subject" type="question" default="GTK_RESPONSE_YES">
+  <_primary>Are you sure you want to send a message without a subject?</_primary>
+  <_secondary>Adding a meaningful Subject line to your messages will give your recipients an idea of what your mail is about.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Send" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="ask-send-only-bcc-contact" type="question" default="GTK_RESPONSE_YES">
+  <_primary>Are you sure you want to send a message with only BCC recipients?</_primary>
+  <_secondary>The contact list you are sending to is configured to hide list recipients.
+
+Many email systems add an Apparently-To header to messages that only have BCC recipients. This header, if added, will list all of your recipients in your message. To avoid this, you should add at least one To: or CC: recipient. </_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Send" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="ask-send-only-bcc" type="warning" default="GTK_RESPONSE_YES">
+  <_primary>Are you sure you want to send a message with only BCC recipients?</_primary>
+  <_secondary>Many email systems add an Apparently-To header to messages that only have BCC recipients. This header, if added, will list all of your recipients to your message anyway. To avoid this, you should add at least one To: or CC: recipient.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Send" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="send-no-recipients" type="warning">
+  <_primary>This message cannot be sent because you have not specified any Recipients</_primary>
+  <_secondary>Please enter a valid email address in the To: field. You can search for email addresses by clicking on the To: button next to the entry box.</_secondary>
+ </error>
+
+ <error id="ask-default-drafts" type="question" default="GTK_RESPONSE_YES">
+  <_primary>Use default drafts folder?</_primary>
+  <_secondary>Unable to open the drafts folder for this account.  Use the system drafts folder instead?</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="Use _Default" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="ask-expunge" type="question" default="GTK_RESPONSE_CANCEL">
+  <_primary>Are you sure you want to permanently remove all the deleted message in folder &quot;{0}&quot;?</_primary>
+  <_secondary>If you continue, you will not be able to recover these messages.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Expunge" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="ask-empty-trash" type="warning" default="GTK_RESPONSE_CANCEL">
+  <_primary>Are you sure you want to permanently remove all the deleted messages in all folders?</_primary>
+  <_secondary>If you continue, you will not be able to recover these messages.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Empty Trash" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="exit-unsaved" type="warning" default="GTK_RESPONSE_NO">
+  <_primary>You have unsent messages, do you wish to quit anyway?</_primary>
+  <_secondary>If you quit, these messages will not be sent until Evolution is started again.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_NO"/>
+  <button stock="gtk-quit" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="camel-exception" type="warning">
+  <_primary>Your message message, with the subject &quot;{0}&quot; was not delivered.</_primary>
+  <_secondary>The message was sent via the &quot;sendmail&quot; external application.  Sendmail reports the following error: status 67: mail not sent.
+The message is stored in the Outbox folder.  Check the message for errors and resend.</_secondary>
+ </error>
+
+ <error id="async-error" type="error">
+  <_primary>Error while {0}.</_primary>
+  <_secondary>{1}.</_secondary>
+ </error>
+
+ <error id="async-error-nodescribe" type="error">
+  <_primary>Error while performing operation.</_primary>
+  <_secondary>{0}.</_secondary>
+ </error>
+
+ <error id="session-message-info" type="info">
+  <_primary>{0}</_primary>
+ </error>
+
+ <error id="session-message-info-cancel" type="info" default="GTK_RESPONSE_CANCEL">
+  <_primary>{0}</_primary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-ok" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="session-message-warning" type="warning">
+  <_primary>{0}</_primary>
+ </error>
+
+ <error id="session-message-warning-cancel" type="warning" default="GTK_RESPONSE_CANCEL">
+  <_primary>{0}</_primary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-ok" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="session-message-error" type="info">
+  <_primary>{0}</_primary>
+ </error>
+
+ <error id="session-message-error-cancel" type="info" default="GTK_RESPONSE_CANCEL">
+  <_primary>{0}</_primary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-ok" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="ask-session-password" type="question" default="GTK_RESPONSE_OK">
+  <_primary>Enter password for {0}</_primary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-ok" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="filter-load-error" type="error">
+  <_primary>Error loading filter definitions.</_primary>
+  <_secondary>{0}</_secondary>
+ </error>
+
+ <error id="no-save-path" type="error">
+  <_primary>Cannot save to directory &quot;{0}&quot;.</_primary>
+  <_secondary>{1}</_secondary>
+ </error>
+
+ <error id="ask-save-path-overwrite" type="error" default="GTK_RESPONSE_CANCEL">
+  <_title>Overwrite file?</_title>
+  <_primary>File exists &quot;{0}&quot;.</_primary>
+  <_secondary>Do you wish to overwrite it?</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button _label="_Overwrite" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="no-create-path" type="error">
+  <_primary>Cannot save to file &quot;{0}&quot;.</_primary>
+  <_secondary>Cannot create the save directory, because &quot;{1}&quot;</_secondary>
+ </error>
+
+ <error id="no-create-tmp-path" type="error">
+  <_primary>Cannot create temporary save directory.</_primary>
+  <_secondary>Because &quot;{1}&quot;.</_secondary>
+ </error>
+
+ <error id="no-write-path-exists" type="error">
+  <_primary>Cannot save to file &quot;{0}&quot;.</_primary>
+  <_secondary>File exists but cannot overwrite it.</_secondary>
+ </error>
+
+ <error id="no-write-path-notfile" type="error">
+  <_primary>Cannot save to file &quot;{0}&quot;.</_primary>
+  <_secondary>File exists but is not a regular file.</_secondary>
+ </error>
+
+ <error id="no-delete-folder" type="error">
+  <_primary>Cannot delete folder &quot;{0}&quot;.</_primary>
+  <_secondary>Because &quot;{1}&quot;.</_secondary>
+ </error>
+
+ <error id="no-delete-spethal-folder" type="error">
+  <_primary>Cannot delete system folder &quot;{0}&quot;.</_primary>
+  <_secondary>System folders are required for Ximian Evolution to function correctly and cannot be renamed, moved, or deleted.</_secondary>
+ </error>
+
+ <error id="no-rename-spethal-folder" type="error">
+  <_primary>Cannot rename or move system folder &quot;{0}&quot;.</_primary>
+  <_secondary>System folders are required for Ximian Evolution to function correctly and cannot be renamed, moved, or deleted.</_secondary>
+ </error>
+
+ <error id="ask-delete-folder" type="question" default="GTK_RESPONSE_CANCEL">
+  <_title>Delete &quot;{0}&quot;?</_title>
+  <_primary>Really delete folder &quot;{0}&quot; and all of its subfolders?</_primary>
+  <_secondary>If you delete the folder, all of its contents and its subfolders contents will be deleted permanently.</_secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-delete" response="GTK_RESPONSE_OK"/>
+ </error>
+
+ <error id="no-rename-folder-exists" type="error">
+  <_primary>Cannot rename &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>A folder named &quot;{1}&quot; already exists. Please use a different name.</_secondary>
+ </error>
+
+ <error id="no-rename-folder" type="error">
+  <_primary>Cannot rename &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>Because &quot;{2}&quot;.</_secondary>
+ </error>
+
+ <error id="no-move-folder-nostore" type="error">
+  <_primary>Cannot move folder &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>Cannot open source &quot;{2}&quot;.</_secondary>
+ </error>
+
+ <error id="no-move-folder-to-nostore" type="error">
+  <_primary>Cannot move folder &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>Cannot open target &quot;{2}&quot;.</_secondary>
+ </error>
+
+ <error id="no-copy-folder-nostore" type="error">
+  <_primary>Cannot copy folder &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>Cannot open source &quot;{2}&quot;.</_secondary>
+ </error>
+
+ <error id="no-copy-folder-to-nostore" type="error">
+  <_primary>Cannot copy folder &quot;{0}&quot; to &quot;{1}&quot;.</_primary>
+  <_secondary>Cannot open target &quot;{2}&quot;.</_secondary>
+ </error>
+
+ <error id="no-create-folder-nostore" type="error">
+  <_primary>Cannot create folder &quot;{0}&quot;.</_primary>
+  <_secondary>Cannot open source &quot;{1}&quot;</_secondary>
+ </error>
+
+ <error id="account-incomplete" type="error">
+  <_primary>Cannot save changes to account.</_primary>
+  <_secondary>You have not filled in all of the required information.</_secondary>
+ </error>
+
+ <error id="account-notunique" type="error">
+  <_primary>Cannot save changes to account.</_primary>
+  <_secondary>You may not create two accounts with the same name.</_secondary>
+ </error>
+
+ <error id="ask-delete-account" type="question" default="GTK_RESPONSE_NO" modal="true">
+  <_title>Delete account?</_title>
+  <_primary>Are you sure you want to delete this account?</_primary>
+  <_secondary>If you proceed, the account information will be deleted permanently.</_secondary>
+  <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
+  <button stock="gtk-no" _label="Don't delete" response="GTK_RESPONSE_NO"/>
+ </error>
+
+ <error id="no-save-signature" type="error">
+  <_primary>Could not save signature file.</_primary>
+  <_secondary>Because &quot;{0}&quot;.</_secondary>
+ </error>
+
+ <error id="signature-notscript" type="error">
+  <_primary>Cannot set signature script &quot;{0}&quot;.</_primary>
+  <_secondary>The script file must exist and be executable.</_secondary>
+ </error>
+
+ <error id="ask-signature-changed" type="question" default="GTK_RESPONSE_YES">
+  <_title>Discard changed?</_title>
+  <_primary>Do you wish to save your changes?</_primary>
+  <_secondary>This signature has been changed, but has not been saved.</_secondary>
+  <button _label="_Discard changes" response="GTK_RESPONSE_NO"/>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-save" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="vfolder-notexist" type="error">
+  <_primary>Cannot edit vFolder &quot;{0}&quot; as it does not exist.</_primary>
+  <_secondary>This folder may have been added implictly, go to the virtual folder editor to add it explictly, if required.</_secondary>
+ </error>
+
+ <error id="vfolder-notunique" type="error">
+  <_primary>Cannot add vFolder &quot;{0}&quot;.</_primary>
+  <_secondary>A folder named &quot;{1}&quot; already exists. Please use a different name.</_secondary>
+ </error>
+
+ <error id="vfolder-updated" type="info">
+  <_primary>vFolders automatically updated.</_primary>
+  <_secondary>The following vFolder(s):
+{0}
+Used the now removed folder:
+    &quot;{1}&quot;
+And have been updated.</_secondary>
+ </error>
+
+ <error id="filter-updated" type="info">
+  <_primary>Mail filters automatically updated.</_primary>
+  <_secondary>The following filter rule(s):
+{0}
+Used the now removed folder:
+    &quot;{1}&quot;
+And have been updated.</_secondary>
+ </error>
+
+</error-list>
+
Index: mail/mail-mt.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-mt.c,v
retrieving revision 1.78
diff -u -3 -r1.78 mail-mt.c
--- mail/mail-mt.c	31 Mar 2004 10:09:04 -0000	1.78
+++ mail/mail-mt.c	16 Apr 2004 09:11:53 -0000
@@ -19,12 +19,14 @@
 #include <gal/widgets/e-gui-utils.h>
 
 #include "e-util/e-msgport.h"
-#include "camel/camel-operation.h"
+#include "widgets/misc/e-error.h"
 
 #include "e-activity-handler.h"
 
-#include "mail-config.h"
 #include "camel/camel-url.h"
+#include "camel/camel-operation.h"
+
+#include "mail-config.h"
 #include "mail-component.h"
 #include "mail-session.h"
 #include "mail-mt.h"
@@ -233,8 +235,7 @@
 void mail_msg_check_error(void *msg)
 {
 	struct _mail_msg *m = msg;
-	char *what = NULL;
-	char *text;
+	char *what;
 	GtkDialog *gd;
 	
 #ifdef MALLOC_CHECK
@@ -254,30 +255,25 @@
 	if (active_errors == NULL)
 		active_errors = g_hash_table_new(NULL, NULL);
 
-	if (m->ops->describe_msg)
-		what = m->ops->describe_msg(m, FALSE);
-
-	if (what) {
-		text = g_strdup_printf(_("Error while '%s':\n%s"), what, camel_exception_get_description(&m->ex));
-		g_free (what);
-	} else
-		text = g_strdup_printf(_("Error while performing operation:\n%s"), camel_exception_get_description(&m->ex));
-
 	/* check to see if we have dialogue already running for this operation */
 	/* we key on the operation pointer, which is at least accurate enough
 	   for the operation type, although it could be on a different object. */
 	if (g_hash_table_lookup(active_errors, m->ops)) {
-		g_warning("Error occured while existing dialogue active:\n%s", text);
-		g_free(text);
+		g_warning("Error occured while existing dialogue active:\n%s", camel_exception_get_description(&m->ex));
 		return;
 	}
 
-	gd = (GtkDialog *)gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", text);
+	if (m->ops->describe_msg
+	    && (what = m->ops->describe_msg(m, FALSE))) {
+		gd = (GtkDialog *)e_error_new(NULL, "mail:async-error", what, camel_exception_get_description(&m->ex), NULL);
+		g_free(what);
+	} else
+		gd = (GtkDialog *)e_error_new(NULL, "mail:async-error-nodescribe", camel_exception_get_description(&m->ex), NULL);
+
 	g_hash_table_insert(active_errors, m->ops, gd);
 	g_signal_connect(gd, "response", G_CALLBACK(error_response), m->ops);
 	g_signal_connect(gd, "destroy", G_CALLBACK(error_destroy), m->ops);
 	gtk_widget_show((GtkWidget *)gd);
-	g_free (text);
 }
 
 void mail_msg_cancel(unsigned int msgid)
Index: mail/mail-session.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-session.c,v
retrieving revision 1.90
diff -u -3 -r1.90 mail-session.c
--- mail/mail-session.c	31 Mar 2004 10:09:04 -0000	1.90
+++ mail/mail-session.c	16 Apr 2004 09:11:53 -0000
@@ -52,6 +52,7 @@
 #include "e-util/e-passwords.h"
 #include "e-util/e-msgport.h"
 #include "em-junk-filter.h"
+#include "widgets/misc/e-error.h"
 
 #define d(x)
 
@@ -270,14 +271,10 @@
 	else
 		title = g_strdup (_("Enter Password"));
 	
-	password_dialog = (GtkDialog *) gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION,
-								GTK_BUTTONS_OK_CANCEL, "%s", m->prompt);
+	password_dialog = (GtkDialog *)e_error_new(NULL, "mail:ask-session-password", m->prompt, NULL);
 	gtk_window_set_title (GTK_WINDOW (password_dialog), title);
-	gtk_dialog_set_default_response (password_dialog, GTK_RESPONSE_OK);
 	g_free (title);
 	
-	gtk_container_set_border_width ((GtkContainer *) password_dialog, 6);
-	
 	m->entry = gtk_entry_new ();
 	gtk_entry_set_visibility ((GtkEntry *) m->entry, !(m->flags & CAMEL_SESSION_PASSWORD_SECRET));
 	g_signal_connect (m->entry, "activate", G_CALLBACK (pass_activate), password_dialog);
@@ -456,11 +453,17 @@
 	message_dialog = NULL;
 }
 
+/* This is kinda ugly/inefficient, but oh well, it works */
+static const char *error_type[] = {
+	"session-message-info", "session-message-warning", "session-message-error",
+	"session-message-info-cancel", "session-message-warning-cancel", "session-message-error-cancel"
+};
+
 static void
 do_user_message (struct _mail_msg *mm)
 {
 	struct _user_message_msg *m = (struct _user_message_msg *)mm;
-	GtkMessageType msg_type;
+	int type;
 	
 	if (!m->ismain && message_dialog != NULL) {
 		e_dlist_addtail (&message_list, (EDListNode *)m);
@@ -469,23 +472,22 @@
 	
 	switch (m->type) {
 	case CAMEL_SESSION_ALERT_INFO:
-		msg_type = GTK_MESSAGE_INFO;
+		type = 0;
 		break;
 	case CAMEL_SESSION_ALERT_WARNING:
-		msg_type = GTK_MESSAGE_WARNING;
+		type = 1;
 		break;
 	case CAMEL_SESSION_ALERT_ERROR:
-		msg_type = GTK_MESSAGE_ERROR;
+		type = 2;
 		break;
 	default:
-		msg_type = GTK_MESSAGE_INFO;
+		type = 0;
 	}
+
+	if (m->allow_cancel)
+		type += 3;
 	
-	message_dialog = (GtkDialog *) gtk_message_dialog_new (
-		NULL, 0, msg_type,
-		m->allow_cancel ? GTK_BUTTONS_OK_CANCEL : GTK_BUTTONS_OK,
-		"%s", m->prompt);
-	gtk_dialog_set_default_response (message_dialog, m->allow_cancel ? GTK_RESPONSE_CANCEL : GTK_RESPONSE_OK);
+	message_dialog = (GtkDialog *)e_error_new(NULL, error_type[type], m->prompt, NULL);
 	g_object_set ((GObject *) message_dialog, "allow_shrink", TRUE, "allow_grow", TRUE, NULL);
 	
 	/* We only need to wait for the result if we allow cancel otherwise show but send result back instantly */
Index: mail/mail-signature-editor.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-signature-editor.c,v
retrieving revision 1.38
diff -u -3 -r1.38 mail-signature-editor.c
--- mail/mail-signature-editor.c	1 Apr 2004 19:47:06 -0000	1.38
+++ mail/mail-signature-editor.c	16 Apr 2004 09:11:54 -0000
@@ -21,7 +21,6 @@
  *
  */
 
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -38,7 +37,7 @@
 #include <bonobo/bonobo-stream-memory.h>
 
 #include <e-util/e-signature-list.h>
-#include <e-util/e-dialog-utils.h>
+#include "widgets/misc/e-error.h"
 
 #include "e-msg-composer.h"
 #include "mail-signature-editor.h"
@@ -83,7 +82,7 @@
 	
 	err = ev->_major != CORBA_NO_EXCEPTION ? bonobo_exception_get_text (ev) : g_strdup (g_strerror (errno));
 	
-	e_notice (NULL, GTK_MESSAGE_ERROR, _("Could not save signature file: %s"), err);
+	e_error_run(NULL, "mail:no-save-signature", err, NULL);
 	g_warning ("Exception while saving signature: %s", err);
 	
 	g_free (err);
@@ -224,25 +223,9 @@
 	CORBA_exception_init (&ev);
 	
 	if (GNOME_GtkHTML_Editor_Engine_hasUndo (editor->engine, &ev)) {
-		GtkWidget *dialog;
 		int button;
-		
-		dialog = gtk_message_dialog_new (GTK_WINDOW (editor->win),
-						 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-						 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
-						 _("This signature has been changed, but hasn't been saved.\n"
-						  "\nDo you wish to save your changes?"));
-		gtk_dialog_add_buttons ((GtkDialog *) dialog,
-					_("_Discard changes"), GTK_RESPONSE_NO,
-					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-					GTK_STOCK_SAVE, GTK_RESPONSE_YES,
-					NULL);
-		gtk_window_set_title ((GtkWindow *) dialog, _("Save signature"));
-		gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_YES);
-		
-		button = gtk_dialog_run ((GtkDialog *) dialog);
-		gtk_widget_destroy (dialog);
-		
+
+		button = e_error_run((GtkWindow *)editor->win, "mail:ask-signature-changed", NULL);
 		exit_dialog_cb (button, editor);
 	} else
 		destroy_editor (editor);
Index: mail/mail-vfolder.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-vfolder.c,v
retrieving revision 1.118
diff -u -3 -r1.118 mail-vfolder.c
--- mail/mail-vfolder.c	31 Mar 2004 10:09:04 -0000	1.118
+++ mail/mail-vfolder.c	16 Apr 2004 09:11:55 -0000
@@ -39,7 +39,7 @@
 #include "em-utils.h"
 
 #include "e-util/e-account-list.h"
-#include "e-util/e-dialog-utils.h"
+#include "widgets/misc/e-error.h"
 
 #include "camel/camel.h"
 #include "camel/camel-vee-folder.h"
@@ -521,11 +521,7 @@
 		GtkWidget *dialog;
 		char *user;
 		
-		dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
-						 _("The following vFolder(s):\n%s"
-						   "Used the removed folder:\n    '%s'\n"
-						   "And have been updated."),
-						 changed->str, uri);
+		dialog = e_error_new(NULL, "mail:vfolder-updated", changed->str, uri, NULL);
 		g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
 		gtk_widget_show (dialog);
 		
@@ -981,8 +977,8 @@
 		g_signal_connect(gd, "response", G_CALLBACK(edit_rule_response), NULL);
 		gtk_widget_show((GtkWidget *)gd);
 	} else {
-		e_notice (NULL, GTK_MESSAGE_WARNING,
-			  _("Trying to edit a vfolder '%s' which doesn't exist."), uri);
+		/* TODO: we should probably just create it ... */
+		e_error_run(NULL, "mail:vfolder-notexist", uri, NULL);
 	}
 
 	if (url)
@@ -1002,15 +998,7 @@
 		}
 
 		if (rule_context_find_rule ((RuleContext *)context, rule->name, rule->source)) {
-			GtkWidget *dialog =
-				gtk_message_dialog_new ((GtkWindow *) w, GTK_DIALOG_DESTROY_WITH_PARENT,
-							GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-							_("Rule name '%s' is not unique, choose another."),
-							rule->name);
-
-			gtk_dialog_run ((GtkDialog *) dialog);
-			gtk_widget_destroy (dialog);
-
+			e_error_run((GtkWindow *)w, "mail:vfolder-notunique", rule->name, NULL);
 			return;
 		}
 
Index: po/POTFILES.in
===================================================================
RCS file: /cvs/gnome/evolution/po/POTFILES.in,v
retrieving revision 1.225
diff -u -3 -r1.225 POTFILES.in
--- po/POTFILES.in	9 Apr 2004 17:46:06 -0000	1.225
+++ po/POTFILES.in	16 Apr 2004 09:11:56 -0000
@@ -239,6 +239,7 @@
 composer/e-msg-composer-select-file.c
 composer/e-msg-composer.c
 composer/evolution-composer.c
+composer/mail-composer-errors.xml.in
 data/evolution.desktop.in.in
 data/evolution.keys.in.in
 designs/OOA/ooa.glade
@@ -251,6 +252,7 @@
 e-util/e-time-utils.c
 filter/filter-datespec.c
 filter/filter-editor.c
+filter/filter-errors.xml.in
 filter/filter-file.c
 filter/filter-filter.c
 filter/filter-folder.c
@@ -306,6 +308,7 @@
 mail/mail-config.c
 mail/mail-config.glade
 mail/mail-crypto.c
+mail/mail-errors.xml.in
 mail/mail-folder-cache.c
 mail/mail-mt.c
 mail/mail-ops.c
Index: widgets/misc/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/widgets/misc/Makefile.am,v
retrieving revision 1.48
diff -u -3 -r1.48 Makefile.am
--- widgets/misc/Makefile.am	8 Apr 2004 07:39:51 -0000	1.48
+++ widgets/misc/Makefile.am	16 Apr 2004 09:11:57 -0000
@@ -2,6 +2,7 @@
 	-I$(top_srcdir)							\
 	-I$(top_srcdir)/a11y/widgets/					\
 	-DMAP_DIR=\""$(imagesdir)"\"					\
+	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"			\
 	-DG_LOG_DOMAIN=__FILE__						\
 	$(SOURCE_SEL_CFLAGS)
 
@@ -24,6 +25,7 @@
 	e-combo-button.h			\
 	e-dateedit.h				\
 	e-dropdown-button.h			\
+	e-error.h				\
 	e-expander.h				\
 	e-image-chooser.h			\
 	e-info-label.h				\
@@ -53,6 +55,7 @@
 	e-combo-button.c			\
 	e-dateedit.c				\
 	e-dropdown-button.c			\
+	e-error.c				\
 	e-expander.c				\
 	e-image-chooser.c			\
 	e-info-label.c				\
Index: widgets/misc/e-error.c
===================================================================
RCS file: widgets/misc/e-error.c
diff -N widgets/misc/e-error.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ widgets/misc/e-error.c	16 Apr 2004 09:11:58 -0000
@@ -0,0 +1,550 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
+ *
+ *  Authors: Michael Zucchi <notzed ximian com>
+ *
+ *  Copyright 2004 Novell Inc. (www.novell.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public
+ *  License as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <glib.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkimage.h>
+
+#include <libgnome/gnome-i18n.h>
+
+#include "e-error.h"
+
+#define d(x) x
+
+struct _e_error_button {
+	struct _e_error_button *next;
+	char *stock;
+	char *label;
+	int response;
+};
+
+struct _e_error {
+	guint32 flags;
+	char *id;
+	int type;
+	int default_response;
+	char *title;
+	char *primary;
+	char *secondary;
+	struct _e_error_button *buttons;
+};
+
+struct _e_error_table {
+	char *domain;
+	GHashTable *errors;
+};
+
+static GHashTable *error_table;
+
+/* ********************************************************************** */
+
+static struct _e_error_button default_ok_button = {
+	NULL, "gtk-ok", NULL, GTK_RESPONSE_OK
+};
+
+static struct _e_error default_errors[] = {
+	{ GTK_DIALOG_MODAL, "error", 3, GTK_RESPONSE_OK, N_("Evolution Error"), "{0}", "{1}", &default_ok_button },
+	{ GTK_DIALOG_MODAL, "error-primary", 3, GTK_RESPONSE_OK, N_("Evolution Error"), "{0}", NULL, &default_ok_button },
+	{ GTK_DIALOG_MODAL, "warning", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", "{1}", &default_ok_button },
+	{ GTK_DIALOG_MODAL, "warning-primary", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", NULL, &default_ok_button },
+};
+
+/* ********************************************************************** */
+
+static struct {
+	char *name;
+	int id;
+} response_map[] = {
+	{ "GTK_RESPONSE_REJECT", GTK_RESPONSE_REJECT },
+	{ "GTK_RESPONSE_ACCEPT", GTK_RESPONSE_ACCEPT },
+	{ "GTK_RESPONSE_OK", GTK_RESPONSE_OK },
+	{ "GTK_RESPONSE_CANCEL", GTK_RESPONSE_CANCEL },
+	{ "GTK_RESPONSE_CLOSE", GTK_RESPONSE_CLOSE },
+	{ "GTK_RESPONSE_YES", GTK_RESPONSE_YES },
+	{ "GTK_RESPONSE_NO", GTK_RESPONSE_NO },
+	{ "GTK_RESPONSE_APPLY", GTK_RESPONSE_APPLY },
+	{ "GTK_RESPONSE_HELP", GTK_RESPONSE_HELP },
+};
+
+static int
+map_response(const char *name)
+{
+	int i;
+	
+	for (i=0;i<sizeof(response_map)/sizeof(response_map[0]);i++)
+		if (!strcmp(name, response_map[i].name))
+			return response_map[i].id;
+
+	return 0;
+}
+
+static struct {
+	const char *name;
+	const char *icon;
+	const char *title;
+} type_map[] = {
+	{ "info", GTK_STOCK_DIALOG_INFO, N_("Evolution Information") },
+	{ "warning", GTK_STOCK_DIALOG_WARNING, N_("Evolution Warning") },
+	{ "question", GTK_STOCK_DIALOG_QUESTION, N_("Evolution Query") },
+	{ "error", GTK_STOCK_DIALOG_ERROR, N_("Evolution Error") },
+};
+
+static int
+map_type(const char *name)
+{
+	int i;
+
+	if (name) {
+		for (i=0;i<sizeof(type_map)/sizeof(type_map[0]);i++)
+			if (!strcmp(name, type_map[i].name))
+				return i;
+	}
+
+	return 3;
+}
+
+static const GList *lang_list;
+
+/* Finds the right node for the current language. */
+static xmlNodePtr
+find_node(xmlNodePtr base, const char *name)
+{
+	const GList *l;
+	xmlNodePtr scan, langc=NULL;
+
+	if (lang_list == NULL)
+		lang_list = gnome_i18n_get_language_list("LC_MESSAGES");
+
+	/* This is, like, way shitful, very slow, terribly inefficient ... sigh */
+	for (l=lang_list;l;l=l->next) {
+		const char *lang = l->data;
+
+		for (scan = base;scan;scan=scan->next) {
+			if (!strcmp(scan->name, name)) {
+				char *xmllang = xmlGetProp(scan, "xml:lang");
+
+				if (xmllang) {
+					if (!strcmp(xmllang, lang)) {
+						xmlFree(xmllang);
+						return scan;
+					}
+					xmlFree(xmllang);
+				} else {
+					langc = scan;
+					if (strcmp(lang, "C") == 0)
+						return scan;
+				}
+			}
+		}
+	}
+
+	return langc;
+}
+
+/*
+  XML format:
+
+ <error id="error-id" type="info|warning|question|error"? response="default_response"? modal="true"? >
+  <_title>Window Title</_title>?
+  <_primary>Primary error text.</_primary>?
+  <_secondary>Secondary error text.</_secondary>?
+  <button ( stock="stock-button-id" | _label="button label" ) response="response_id"? > *
+ </error>
+
+ Because we use intltool we need to do some weird shit to do with
+ languages.  title, primary and secondary will be expanded into
+ per-language tags, specified using xml:lang property.
+
+ The _label property will remain as plain english and we need to use
+ gettext to extract it.
+*/
+static void
+ee_load(const char *path)
+{
+	xmlDocPtr doc;
+	xmlNodePtr root, error, scan, node;
+	struct _e_error *e;
+	struct _e_error_button *lastbutton;
+	struct _e_error_table *table;
+	char *tmp;
+
+	d(printf("loading error file %s\n", path));
+	
+	doc = xmlParseFile(path);
+	if (doc == NULL) {
+		g_warning("Error file '%s' not found", path);
+		return;
+	}
+
+	root = xmlDocGetRootElement(doc);
+	if (root == NULL
+	    || strcmp(root->name, "error-list") != 0
+	    || (tmp = xmlGetProp(root, "domain")) == NULL) {
+		g_warning("Error file '%s' invalid format", path);
+		xmlFreeDoc(doc);
+		return;
+	}
+
+	table = g_malloc0(sizeof(*table));
+	table->domain = g_strdup(tmp);
+	g_free(tmp);
+	table->errors = g_hash_table_new(g_str_hash, g_str_equal);
+
+	for (error = root->children;error;error = error->next) {
+		if (!strcmp(error->name, "error")) {
+			tmp = xmlGetProp(error, "id");
+			if (tmp == NULL)
+				continue;
+
+			e = g_malloc0(sizeof(*e));
+			e->id = g_strdup(tmp);
+			xmlFree(tmp);
+			lastbutton = (struct _e_error_button *)&e->buttons;
+
+			tmp = xmlGetProp(error, "modal");
+			if (tmp) {
+				if (!strcmp(tmp, "true"))
+					e->flags |= GTK_DIALOG_MODAL;
+				xmlFree(tmp);
+			}
+
+			tmp = xmlGetProp(error, "type");
+			e->type = map_type(tmp);
+			if (tmp)
+				xmlFree(tmp);
+
+			tmp = xmlGetProp(error, "default");
+			if (tmp) {
+				e->default_response = map_response(tmp);
+				xmlFree(tmp);
+			}
+
+			node = find_node(error->children, "primary");
+			if (node && (tmp = xmlNodeGetContent(node))) {
+				e->primary = g_strdup(tmp);
+				xmlFree(tmp);
+			}
+
+			node = find_node(error->children, "secondary");
+			if (node && (tmp = xmlNodeGetContent(node))) {
+				e->secondary = g_strdup(tmp);
+				xmlFree(tmp);
+			}
+
+			node = find_node(error->children, "title");
+			if (node && (tmp = xmlNodeGetContent(node))) {
+				e->title = g_strdup(tmp);
+				xmlFree(tmp);
+			}
+
+			for (scan = error->children;scan;scan=scan->next) {
+				if (!strcmp(scan->name, "button")) {
+					struct _e_error_button *b;
+
+					b = g_malloc0(sizeof(*b));
+					tmp = xmlGetProp(scan, "stock");
+					if (tmp) {
+						b->stock = g_strdup(tmp);
+						xmlFree(tmp);
+					}
+					tmp = xmlGetProp(scan, "label");
+					if (tmp) {
+						b->label = g_strdup(_(tmp));
+						xmlFree(tmp);
+					}
+					tmp = xmlGetProp(scan, "response");
+					if (tmp) {
+						b->response = map_response(tmp);
+						xmlFree(tmp);
+					}
+
+					if (b->stock == NULL && b->label == NULL) {
+						g_warning("Error file '%s': missing button details in error '%s'", path, e->id);
+						g_free(b->stock);
+						g_free(b->label);
+						g_free(b);
+					} else {
+						lastbutton->next = b;
+						lastbutton = b;
+					}
+				}
+			}
+
+			g_hash_table_insert(table->errors, e->id, e);
+		}
+	}
+
+	xmlFreeDoc(doc);
+
+	g_hash_table_insert(error_table, table->domain, table);
+}
+
+static void
+ee_load_tables(void)
+{
+	DIR *dir;
+	struct dirent *d;
+	const char *base = EVOLUTION_PRIVDATADIR "/errors";
+	struct _e_error_table *table;
+	int i;
+
+	if (error_table != NULL)
+		return;
+
+	error_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+	/* setup system error types */
+	table = g_malloc0(sizeof(*table));
+	table->domain = "system";
+	table->errors = g_hash_table_new(g_str_hash, g_str_equal);
+	for (i=0;i<sizeof(default_errors)/sizeof(default_errors[0]);i++)
+		g_hash_table_insert(table->errors, default_errors[i].id, &default_errors[i]);
+	g_hash_table_insert(error_table, table->domain, table);
+
+	/* look for installed error tables */
+	dir = opendir(base);
+	if (dir == NULL)
+		return;
+
+	while ( (d = readdir(dir)) ) {
+		char *path;
+
+		if (d->d_name[0] == '.')
+			continue;
+
+		path = g_build_filename(base, d->d_name, NULL);
+		ee_load(path);
+		g_free(path);
+	}
+
+	closedir(dir);
+}
+
+static void
+ee_build_label(GString *out, const char *fmt, GPtrArray *args)
+{
+	const char *end, *newstart;
+	int id;
+
+	while (fmt
+	       && (newstart = strchr(fmt, '{'))
+	       && (end = strchr(newstart+1, '}'))) {
+		g_string_append_len(out, fmt, newstart-fmt);
+		id = atoi(newstart+1);
+		if (id < args->len)
+			g_string_append(out, args->pdata[id]);
+		else
+			g_warning("Error references argument %d not supplied by caller", id);
+		fmt = end+1;
+	}
+
+	g_string_append(out, fmt);
+}
+
+GtkWidget *
+e_error_newv(GtkWindow *parent, const char *tag, const char *arg0, va_list ap)
+{
+	struct _e_error_table *table;
+	struct _e_error *e;
+	struct _e_error_button *b;
+	GtkWidget *hbox, *w;
+	char *tmp, *domain, *id;
+	GString *out;
+	GPtrArray *args;
+	GtkDialog *dialog;
+
+	if (error_table == NULL)
+		ee_load_tables();
+
+	dialog = (GtkDialog *)gtk_dialog_new();
+	gtk_dialog_set_has_separator(dialog, FALSE);
+
+	if (parent)
+		gtk_window_set_transient_for((GtkWindow *)dialog, parent);
+
+	domain = alloca(strlen(tag)+1);
+	strcpy(domain, tag);
+	id = strchr(domain, ':');
+	if (id)
+		*id++ = 0;
+
+	if ( id == NULL
+	     || (table = g_hash_table_lookup(error_table, domain)) == NULL
+	     || (e = g_hash_table_lookup(table->errors, id)) == NULL) {
+		/* setup a dummy error */
+		tmp = g_strdup_printf(_("<span weight=\"bold\">Internal error, unknown error '%s' requested</span>"), tag);
+		w = gtk_label_new(NULL);
+		gtk_label_set_line_wrap((GtkLabel *)w, TRUE);
+		gtk_label_set_markup((GtkLabel *)w, tmp);
+		gtk_widget_show(w);
+		gtk_box_pack_start((GtkBox *)dialog->vbox, w, TRUE, TRUE, 12);
+
+		return (GtkWidget *)dialog;
+	}
+
+	if (e->flags & GTK_DIALOG_MODAL)
+		gtk_window_set_modal((GtkWindow *)dialog, TRUE);
+	gtk_window_set_destroy_with_parent((GtkWindow *)dialog, TRUE);
+
+	b = e->buttons;
+	if (b == NULL) {
+		gtk_dialog_add_button(dialog, GTK_STOCK_OK, GTK_RESPONSE_OK);
+	} else {
+		for (b = e->buttons;b;b=b->next) {
+			/* TODO: allow stock icons on non-stock labels */
+			if (b->stock)
+				gtk_dialog_add_button(dialog, b->stock, b->response);
+			else
+				gtk_dialog_add_button(dialog, b->label, b->response);
+		}
+	}
+
+	if (e->default_response)
+		gtk_dialog_set_default_response(dialog, e->default_response);
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_container_set_border_width((GtkContainer *)hbox, 12);
+
+	w = gtk_image_new_from_stock(type_map[e->type].icon, GTK_ICON_SIZE_DIALOG);
+	gtk_box_pack_start((GtkBox *)hbox, w, TRUE, TRUE, 12);
+
+	args = g_ptr_array_new();
+	tmp = (char *)arg0;
+	while (tmp) {
+		g_ptr_array_add(args, tmp);
+		tmp = va_arg(ap, char *);
+	}
+
+	out = g_string_new("");
+
+	if (e->title) {
+		ee_build_label(out, _(e->title), args);
+		gtk_window_set_title((GtkWindow *)dialog, _(e->title));
+		g_string_truncate(out, 0);
+	} else
+		gtk_window_set_title((GtkWindow *)dialog, _(type_map[e->type].title));
+
+
+	if (e->primary) {
+		g_string_append(out, "<span weight=\"bold\" size=\"larger\">");
+		ee_build_label(out, _(e->primary), args);
+		g_string_append(out, "</span>\n\n");
+	}
+
+	if (e->secondary)
+		ee_build_label(out, _(e->secondary), args);
+
+	g_ptr_array_free(args, TRUE);
+
+	w = gtk_label_new(NULL);
+	gtk_label_set_line_wrap((GtkLabel *)w, TRUE);
+	gtk_label_set_markup((GtkLabel *)w, out->str);
+	g_string_free(out, TRUE);
+	gtk_box_pack_start((GtkBox *)hbox, w, FALSE, FALSE, 0);
+	gtk_widget_show_all(hbox);
+
+	gtk_box_pack_start((GtkBox *)dialog->vbox, hbox, TRUE, TRUE, 0);
+
+	return (GtkWidget *)dialog;
+}
+
+/**
+ * e_error_new:
+ * @parent: 
+ * @tag: error identifier
+ * @arg0: The first argument for the error formatter.  The list must
+ * be NULL terminated.
+ * 
+ * Creates a new error widget.  The @tag argument is used to determine
+ * which error to use, it is in the format domain:error-id.  The NULL
+ * terminated list of arguments, starting with @arg0 is used to fill
+ * out the error definition.
+ * 
+ * Return value: A GtkDialog which can be used for showing an error
+ * dialog asynchronously.
+ **/
+struct _GtkWidget *
+e_error_new(struct _GtkWindow *parent, const char *tag, const char *arg0, ...)
+{
+	GtkWidget *w;
+	va_list ap;
+
+	va_start(ap, arg0);
+	w = e_error_newv(parent, tag, arg0, ap);
+	va_end(ap);
+
+	return w;
+}
+
+int
+e_error_runv(GtkWindow *parent, const char *tag, const char *arg0, va_list ap)
+{
+	GtkWidget *w;
+	int res;
+
+	w = e_error_newv(parent, tag, arg0, ap);
+
+	res = gtk_dialog_run((GtkDialog *)w);
+	gtk_widget_destroy(w);
+
+	return res;
+}
+
+/**
+ * e_error_run:
+ * @parent: 
+ * @tag: 
+ * @arg0: 
+ * 
+ * Sets up, displays, runs and destroys a standard evolution error
+ * dialog based on @tag, which is in the format domain:error-id.
+ * 
+ * Return value: The response id of the button pressed.
+ **/
+int
+e_error_run(GtkWindow *parent, const char *tag, const char *arg0, ...)
+{
+	GtkWidget *w;
+	va_list ap;
+	int res;
+
+	va_start(ap, arg0);
+	w = e_error_newv(parent, tag, arg0, ap);
+	va_end(ap);
+
+	res = gtk_dialog_run((GtkDialog *)w);
+	gtk_widget_destroy(w);
+
+	return res;
+}
+
+
Index: widgets/misc/e-error.h
===================================================================
RCS file: widgets/misc/e-error.h
diff -N widgets/misc/e-error.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ widgets/misc/e-error.h	16 Apr 2004 09:11:58 -0000
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
+ *
+ *  Authors: Michael Zucchi <notzed ximian com>
+ *
+ *  Copyright 2004 Novell Inc. (www.novell.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public
+ *  License as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _E_ERROR_H
+#define _E_ERROR_H
+
+#include <stdarg.h>
+
+struct _GtkWindow;
+
+#define E_ERROR_WARNING "system:warning"
+#define E_ERROR_WARNING_PRIMARY "system:warning-primary"
+#define E_ERROR_ERROR "system:error"
+#define E_ERROR_ERROR_PRIMARY "system:error-primary"
+
+struct _GtkWidget *e_error_new(struct _GtkWindow *parent, const char *tag, const char *arg0, ...);
+struct _GtkWidget *e_error_newv(struct _GtkWindow *parent, const char *tag, const char *arg0, va_list ap);
+
+int e_error_run(struct _GtkWindow *parent, const char *tag, const char *arg0, ...);
+int e_error_runv(struct _GtkWindow *parent, const char *tag, const char *arg0, va_list ap);
+
+#endif /* !_E_ERROR_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]