aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--e-util/ChangeLog5
-rw-r--r--e-util/Makefile.am21
-rw-r--r--e-util/e-error-tool.c134
-rw-r--r--e-util/e-error.c628
-rw-r--r--e-util/e-error.h57
-rw-r--r--e-util/e-system.error.xml22
6 files changed, 721 insertions, 146 deletions
diff --git a/e-util/ChangeLog b/e-util/ChangeLog
index c2faeeb73b..5f5c407550 100644
--- a/e-util/ChangeLog
+++ b/e-util/ChangeLog
@@ -1,3 +1,8 @@
+2005-05-16 Not Zed <NotZed@Ximian.com>
+
+ * Makefile.am: added e-error.[ch], removed e-error-tool, and added
+ e-system.error.xml
+
2005-05-13 Not Zed <NotZed@Ximian.com>
* e-popup.c (emph_popup_factory): if we have a factory method
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 5ffedc9415..888cd47cd5 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -12,6 +12,7 @@ INCLUDES = \
-DEVOLUTION_ICONSDIR=\""$(imagesdir)"\" \
-DEVOLUTION_CATEGORY_ICONS=\""$(imagesdir)/categories"\" \
-DEVOLUTION_PLUGINDIR=\""$(plugindir)"\" \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
-DG_LOG_DOMAIN=\"e-utils\" \
$(GNOME_PILOT_CFLAGS) \
$(E_UTIL_CFLAGS)
@@ -30,6 +31,7 @@ eutilinclude_HEADERS = \
e-corba-utils.h \
e-dialog-utils.h \
e-dialog-widgets.h \
+ e-error.h \
e-event.h \
e-folder-map.h \
e-fsutils.h \
@@ -68,6 +70,7 @@ libeutil_la_SOURCES = \
e-corba-utils.c \
e-dialog-utils.c \
e-dialog-widgets.c \
+ e-error.c \
e-event.c \
e-folder-map.c \
e-fsutils.c \
@@ -116,24 +119,18 @@ endif
libeconduit_la_SOURCES = $(pilot_compile)
-# e-error-tool
-
-e_error_tool_SOURCES = \
- e-error-tool.c
-
-e_error_tool_LDADD = \
- $(GNOME_FULL_LIBS)
-
-noinst_PROGRAMS = \
- e-error-tool
+error_DATA = e-system.error
+errordir = $(privdatadir)/errors
+@EVO_PLUGIN_RULE@
EXTRA_DIST = \
+ e-system.error.xml \
$(pilot_sources) \
ChangeLog.pre-1-4 \
e-util-marshal.list
-BUILT_SOURCES = $(MARSHAL_GENERATED)
+BUILT_SOURCES = $(MARSHAL_GENERATED) $(error_DATA)
CLEANFILES = $(BUILT_SOURCES)
-dist-hook: e-error-tool
+dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/e-util/e-error-tool.c b/e-util/e-error-tool.c
deleted file mode 100644
index 4544b8b158..0000000000
--- a/e-util/e-error-tool.c
+++ /dev/null
@@ -1,134 +0,0 @@
-
-#include <errno.h>
-
-#include <stdio.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-#include <unistd.h>
-
-#include <libxml/parser.h>
-#include <libxml/xmlmemory.h>
-
-#include <glib.h>
-
-static void
-dump_cstring(FILE *out, const char *tmp)
-{
- const char *p;
- int c;
-
- fprintf(out, "char *s = N_(\"");
- p = tmp;
- while ( (c = *p++) ) {
- switch (c) {
- case '\n':
- fprintf(out, "\\n\"\n\t\"");
- break;
- case '\r':
- fprintf(out, "\\r");
- break;
- case '\t':
- fprintf(out, "\\t");
- break;
- case '"':
- case '\\':
- fprintf(out, "\\%c", c);
- break;
- default:
- fputc(c, out);
- }
- }
-
- fprintf(out, "\");\n");
-}
-
-static void
-extract_strings(FILE *out, const char *path)
-{
- xmlDocPtr doc;
- xmlNodePtr root, error, scan;
- char *tmp, *domain;
-
- 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
- || (domain = xmlGetProp(root, "domain")) == NULL) {
- g_warning("Error file '%s' invalid format", path);
- xmlFreeDoc(doc);
- return;
- }
-
- for (error = root->children;error;error = error->next) {
- char *id;
-
- if (strcmp(error->name, "error") != 0)
- continue;
-
- id = xmlGetProp(error, "id");
- if (id == NULL) {
- g_warning("Error format invalid, missing error id");
- _exit(1);
- }
-
- for (scan = error->children;scan;scan=scan->next) {
- if (!strcmp(scan->name, "primary")
- || !strcmp(scan->name, "secondary")
- || !strcmp(scan->name, "title")) {
- fprintf(out, "/* %s:%s %s */\n", domain, id, scan->name);
- tmp = xmlNodeGetContent(scan);
- if (tmp) {
- dump_cstring(out, tmp);
- xmlFree(tmp);
- }
- } else if (!strcmp(scan->name, "button")) {
- tmp = xmlGetProp(scan, "label");
- if (tmp) {
- dump_cstring(out, tmp);
- xmlFree(tmp);
- }
- }
- }
- xmlFree(id);
- }
-
- xmlFree(domain);
-
- xmlFreeDoc(doc);
-}
-
-int main(int argc, char **argv)
-{
- int i;
- FILE *out;
-
- for (i=1;i<argc;i++) {
- char *orig_name, *name;
-
- orig_name = g_path_get_basename (argv[i]);
- name = g_strdup_printf("%s.h", orig_name);
- g_free (orig_name);
-
- out = fopen(name, "w");
- if (out == NULL) {
- fprintf(stderr, "Error creating %s: %s\n", name, strerror(errno));
- return 1;
- }
- extract_strings(out, argv[i]);
- if (fclose(out) != 0) {
- fprintf(stderr, "Error writing to %s: %s\n", name, strerror(errno));
- return 1;
- }
- g_free(name);
- }
-
- return 0;
-}
diff --git a/e-util/e-error.c b/e-util/e-error.c
new file mode 100644
index 0000000000..f7dc888609
--- /dev/null
+++ b/e-util/e-error.c
@@ -0,0 +1,628 @@
+/* -*- 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <glib.h>
+#include <gtk/gtkbutton.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 <libgnome/gnome-url.h>
+
+#include "e-error.h"
+
+#define d(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;
+ char *help_uri;
+ struct _e_error_button *buttons;
+};
+
+struct _e_error_table {
+ char *domain;
+ char *translation_domain;
+ GHashTable *errors;
+};
+
+static GHashTable *error_table;
+static GSList *ee_parent_list;
+
+/* ********************************************************************** */
+
+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}", NULL, &default_ok_button },
+ { GTK_DIALOG_MODAL, "error-primary", 3, GTK_RESPONSE_OK, N_("Evolution Error"), "{0}", NULL, NULL, &default_ok_button },
+ { GTK_DIALOG_MODAL, "warning", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", "{1}", NULL, &default_ok_button },
+ { GTK_DIALOG_MODAL, "warning-primary", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", NULL, 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;
+}
+
+/*
+ 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>?
+ <help uri="help uri"/> ?
+ <button stock="stock-button-id"? label="button label"? response="response_id"? /> *
+ </error>
+
+ The tool e-error-tool is used to extract the translatable strings for
+ translation.
+
+*/
+static void
+ee_load(const char *path)
+{
+ xmlDocPtr doc;
+ xmlNodePtr root, error, scan;
+ 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_hash_table_lookup(error_table, tmp);
+ if (table == NULL) {
+ char *tmp2;
+
+ table = g_malloc0(sizeof(*table));
+ table->domain = g_strdup(tmp);
+ table->errors = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(error_table, table->domain, table);
+
+ tmp2 = xmlGetProp(root, "translation-domain");
+ if (tmp2) {
+ table->translation_domain = g_strdup(tmp2);
+ xmlFree(tmp2);
+
+ tmp2 = xmlGetProp(root, "translation-localedir");
+ if (tmp2) {
+ bindtextdomain(table->translation_domain, tmp2);
+ xmlFree(tmp2);
+ }
+ }
+ } else
+ g_warning("Error file '%s', domain '%s' already used, merging", path, tmp);
+ xmlFree(tmp);
+
+ 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);
+ }
+
+ for (scan = error->children;scan;scan=scan->next) {
+ if (!strcmp(scan->name, "primary")) {
+ if ((tmp = xmlNodeGetContent(scan))) {
+ e->primary = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp(scan->name, "secondary")) {
+ if ((tmp = xmlNodeGetContent(scan))) {
+ e->secondary = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp(scan->name, "title")) {
+ if ((tmp = xmlNodeGetContent(scan))) {
+ e->title = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp(scan->name, "help")) {
+ tmp = xmlGetProp(scan, "uri");
+ if (tmp) {
+ e->help_uri = g_strdup(tmp);
+ xmlFree(tmp);
+ }
+ } else 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(dgettext(table->translation_domain, 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);
+}
+
+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 = "builtin";
+ 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);
+}
+
+/* unfortunately, gmarkup_escape doesn't expose its gstring based api :( */
+static void
+ee_append_text(GString *out, const char *text)
+{
+ char c;
+
+ while ( (c=*text++) ) {
+ if (c == '<')
+ g_string_append(out, "&lt;");
+ else if (c == '>')
+ g_string_append(out, "&gt;");
+ else if (c == '"')
+ g_string_append(out, "&quot;");
+ else if (c == '\'')
+ g_string_append(out, "&apos;");
+ else if (c == '&')
+ g_string_append(out, "&amp;");
+ else
+ g_string_append_c(out, c);
+ }
+}
+
+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)
+ ee_append_text(out, args->pdata[id]);
+ else
+ g_warning("Error references argument %d not supplied by caller", id);
+ fmt = end+1;
+ }
+
+ g_string_append(out, fmt);
+}
+
+static void
+ee_response(GtkWidget *w, guint button, struct _e_error *e)
+{
+ GError *err = NULL;
+
+ if (button == GTK_RESPONSE_HELP) {
+ g_signal_stop_emission_by_name(w, "response");
+ gnome_url_show(e->help_uri, &err);
+ if (err) {
+ g_warning("Unable to run help uri: %s", err->message);
+ g_error_free(err);
+ }
+ }
+}
+
+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);
+
+ gtk_widget_ensure_style ((GtkWidget *)dialog);
+ gtk_container_set_border_width ((GtkContainer *)(dialog->vbox), 0);
+ gtk_container_set_border_width ((GtkContainer *)(dialog->action_area), 12);
+
+ if (parent == NULL && ee_parent_list)
+ parent = (GtkWindow *)ee_parent_list->data;
+ if (parent)
+ gtk_window_set_transient_for((GtkWindow *)dialog, parent);
+ else
+ g_warning("No parent set, or default parent available for error dialog");
+
+ 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_selectable((GtkLabel *)w, TRUE);
+ 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);
+
+ if (e->help_uri) {
+ w = gtk_dialog_add_button(dialog, GTK_STOCK_HELP, GTK_RESPONSE_HELP);
+ g_signal_connect(dialog, "response", G_CALLBACK(ee_response), e);
+ }
+
+ 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) {
+ if (b->stock) {
+ if (b->label) {
+#if 0
+ /* FIXME: So although this looks like it will work, it wont.
+ Need to do it the hard way ... it also breaks the
+ default_response stuff */
+ w = gtk_button_new_from_stock(b->stock);
+ gtk_button_set_label((GtkButton *)w, b->label);
+ gtk_widget_show(w);
+ gtk_dialog_add_action_widget(dialog, w, b->response);
+#endif
+ gtk_dialog_add_button(dialog, b->label, b->response);
+ } else
+ 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_misc_set_alignment((GtkMisc *)w, 0.0, 0.0);
+ 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, dgettext(table->translation_domain, e->title), args);
+ gtk_window_set_title((GtkWindow *)dialog, out->str);
+ g_string_truncate(out, 0);
+ } else
+ gtk_window_set_title((GtkWindow *)dialog, dgettext(table->translation_domain, type_map[e->type].title));
+
+
+ if (e->primary) {
+ g_string_append(out, "<span weight=\"bold\" size=\"larger\">");
+ ee_build_label(out, dgettext(table->translation_domain, e->primary), args);
+ g_string_append(out, "</span>\n\n");
+ }
+
+ if (e->secondary)
+ ee_build_label(out, dgettext(table->translation_domain, e->secondary), args);
+
+ g_ptr_array_free(args, TRUE);
+
+ w = gtk_label_new(NULL);
+ gtk_label_set_selectable((GtkLabel *)w, TRUE);
+ 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;
+}
+
+static void
+remove_parent(GtkWidget *w, GtkWidget *parent)
+{
+ ee_parent_list = g_slist_remove(ee_parent_list, parent);
+}
+
+/**
+ * e_error_default_parent:
+ * @parent:
+ *
+ * Bit of a hack, set a default parent that will be used to parent any
+ * error boxes if none is supplied.
+ *
+ * This may be called multiple times, and the last call will be the
+ * main default. This function will keep track of the parents
+ * destruction state.
+ **/
+void
+e_error_default_parent(struct _GtkWindow *parent)
+{
+ if (g_slist_find(ee_parent_list, parent) == NULL) {
+ ee_parent_list = g_slist_prepend(ee_parent_list, parent);
+ g_signal_connect(parent, "destroy", G_CALLBACK(remove_parent), parent);
+ }
+}
+
diff --git a/e-util/e-error.h b/e-util/e-error.h
new file mode 100644
index 0000000000..fe097656ab
--- /dev/null
+++ b/e-util/e-error.h
@@ -0,0 +1,57 @@
+/* -*- 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;
+
+/*
+ * Some standard errors, if these are altered or added to,
+ * update devel-docs/misc/errors.txt
+ *
+ * Several more basic ones are needed.
+ */
+
+#define E_ERROR_WARNING "builtin:warning"
+#define E_ERROR_WARNING_PRIMARY "builtin:warning-primary"
+#define E_ERROR_ERROR "builtin:error"
+#define E_ERROR_ERROR_PRIMARY "builtin:error-primary"
+
+/* takes filename, returns OK if yes */
+#define E_ERROR_ASK_FILE_EXISTS_OVERWRITE "system:ask-save-file-exists-overwrite"
+/* takes filename, reason */
+#define E_ERROR_NO_SAVE_FILE "system:no-save-file"
+/* takes filename, reason */
+#define E_ERROR_NO_LOAD_FILE "system:no-save-file"
+
+/* Note that all errors returned are standard GtkDialoge's */
+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);
+
+void e_error_default_parent(struct _GtkWindow *parent);
+
+#endif /* !_E_ERROR_H */
diff --git a/e-util/e-system.error.xml b/e-util/e-system.error.xml
new file mode 100644
index 0000000000..7a2022af7f
--- /dev/null
+++ b/e-util/e-system.error.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<error-list domain="system">
+
+ <error id="ask-save-file-exists-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-save-file" type="error">
+ <_primary>Cannot save file &quot;{0}&quot;.</_primary>
+ <_secondary>Because &quot;{1}&quot;.</_secondary>
+ </error>
+
+ <error id="no-load-file" type="error">
+ <_primary>Cannot open file &quot;{0}&quot;.</_primary>
+ <_secondary>Because &quot;{1}&quot;.</_secondary>
+ </error>
+
+</error-list>