aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-alert.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-alert.c')
-rw-r--r--e-util/e-alert.c698
1 files changed, 698 insertions, 0 deletions
diff --git a/e-util/e-alert.c b/e-util/e-alert.c
new file mode 100644
index 0000000000..cc7488744e
--- /dev/null
+++ b/e-util/e-alert.c
@@ -0,0 +1,698 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-xml-utils.h>
+
+#include "e-util.h"
+#include "e-util-private.h"
+#include "e-alert.h"
+
+#define d(x)
+
+struct _EAlert
+{
+ gchar *tag;
+ GPtrArray *args;
+};
+
+void
+e_alert_free (EAlert *alert)
+{
+ if (alert == NULL)
+ return;
+ g_free (alert->tag);
+ /* arg strings will be freed automatically since we set a free func when
+ * creating the ptr array */
+ g_ptr_array_free (alert->args, TRUE);
+}
+
+struct _e_alert_button {
+ struct _e_alert_button *next;
+ const gchar *stock;
+ const gchar *label;
+ gint response;
+};
+
+struct _e_alert {
+ guint32 flags;
+ const gchar *id;
+ gint type;
+ gint default_response;
+ const gchar *title;
+ const gchar *primary;
+ const gchar *secondary;
+ const gchar *help_uri;
+ gboolean scroll;
+ struct _e_alert_button *buttons;
+};
+
+struct _e_alert_table {
+ const gchar *domain;
+ const gchar *translation_domain;
+ GHashTable *alerts;
+};
+
+static GHashTable *alert_table;
+
+/* ********************************************************************** */
+
+static struct _e_alert_button default_ok_button = {
+ NULL, "gtk-ok", NULL, GTK_RESPONSE_OK
+};
+
+static struct _e_alert default_alerts[] = {
+ { GTK_DIALOG_MODAL, "error", 3, GTK_RESPONSE_OK, N_("Evolution Error"), "{0}", "{1}", NULL, FALSE, &default_ok_button },
+ { GTK_DIALOG_MODAL, "error-primary", 3, GTK_RESPONSE_OK, N_("Evolution Error"), "{0}", NULL, NULL, FALSE, &default_ok_button },
+ { GTK_DIALOG_MODAL, "warning", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", "{1}", NULL, FALSE, &default_ok_button },
+ { GTK_DIALOG_MODAL, "warning-primary", 1, GTK_RESPONSE_OK, N_("Evolution Warning"), "{0}", NULL, NULL, FALSE, &default_ok_button },
+};
+
+/* ********************************************************************** */
+
+static struct {
+ const gchar *name;
+ gint 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 gint
+map_response(const gchar *name)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (response_map); i++)
+ if (!strcmp(name, response_map[i].name))
+ return response_map[i].id;
+
+ return 0;
+}
+
+static struct {
+ const gchar *name;
+ const gchar *icon;
+} type_map[] = {
+ { "info", GTK_STOCK_DIALOG_INFO },
+ { "warning", GTK_STOCK_DIALOG_WARNING },
+ { "question", GTK_STOCK_DIALOG_QUESTION },
+ { "error", GTK_STOCK_DIALOG_ERROR },
+};
+
+static gint
+map_type(const gchar *name)
+{
+ gint i;
+
+ if (name) {
+ for (i = 0; i < G_N_ELEMENTS (type_map); 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 gchar *path)
+{
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root, error, scan;
+ struct _e_alert *e;
+ struct _e_alert_button *lastbutton;
+ struct _e_alert_table *table;
+ gchar *tmp;
+
+ d(printf("loading error file %s\n", path));
+
+ doc = e_xml_parse_file (path);
+ if (doc == NULL) {
+ g_warning("Error file '%s' not found", path);
+ return;
+ }
+
+ root = xmlDocGetRootElement(doc);
+ if (root == NULL
+ || strcmp((gchar *)root->name, "error-list") != 0
+ || (tmp = (gchar *)xmlGetProp(root, (const guchar *)"domain")) == NULL) {
+ g_warning("Error file '%s' invalid format", path);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ table = g_hash_table_lookup(alert_table, tmp);
+ if (table == NULL) {
+ gchar *tmp2;
+
+ table = g_malloc0(sizeof(*table));
+ table->domain = g_strdup(tmp);
+ table->alerts = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(alert_table, (gpointer) table->domain, table);
+
+ tmp2 = (gchar *)xmlGetProp(root, (const guchar *)"translation-domain");
+ if (tmp2) {
+ table->translation_domain = g_strdup(tmp2);
+ xmlFree(tmp2);
+
+ tmp2 = (gchar *)xmlGetProp(root, (const guchar *)"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((gchar *)error->name, "error")) {
+ tmp = (gchar *)xmlGetProp(error, (const guchar *)"id");
+ if (tmp == NULL)
+ continue;
+
+ e = g_malloc0(sizeof(*e));
+ e->id = g_strdup(tmp);
+ e->scroll = FALSE;
+
+ xmlFree(tmp);
+ lastbutton = (struct _e_alert_button *)&e->buttons;
+
+ tmp = (gchar *)xmlGetProp(error, (const guchar *)"modal");
+ if (tmp) {
+ if (!strcmp(tmp, "true"))
+ e->flags |= GTK_DIALOG_MODAL;
+ xmlFree(tmp);
+ }
+
+ tmp = (gchar *)xmlGetProp(error, (const guchar *)"type");
+ e->type = map_type(tmp);
+ if (tmp)
+ xmlFree(tmp);
+
+ tmp = (gchar *)xmlGetProp(error, (const guchar *)"default");
+ if (tmp) {
+ e->default_response = map_response(tmp);
+ xmlFree(tmp);
+ }
+
+ tmp = (gchar *)xmlGetProp(error, (const guchar *)"scroll");
+ if (tmp) {
+ if (!strcmp(tmp, "yes"))
+ e->scroll = TRUE;
+ xmlFree(tmp);
+ }
+
+ for (scan = error->children;scan;scan=scan->next) {
+ if (!strcmp((gchar *)scan->name, "primary")) {
+ if ((tmp = (gchar *)xmlNodeGetContent(scan))) {
+ e->primary = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp((gchar *)scan->name, "secondary")) {
+ if ((tmp = (gchar *)xmlNodeGetContent(scan))) {
+ e->secondary = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp((gchar *)scan->name, "title")) {
+ if ((tmp = (gchar *)xmlNodeGetContent(scan))) {
+ e->title = g_strdup(dgettext(table->translation_domain, tmp));
+ xmlFree(tmp);
+ }
+ } else if (!strcmp((gchar *)scan->name, "help")) {
+ tmp = (gchar *)xmlGetProp(scan, (const guchar *)"uri");
+ if (tmp) {
+ e->help_uri = g_strdup(tmp);
+ xmlFree(tmp);
+ }
+ } else if (!strcmp((gchar *)scan->name, "button")) {
+ struct _e_alert_button *b;
+ gchar *label = NULL;
+ gchar *stock = NULL;
+
+ b = g_malloc0(sizeof(*b));
+ tmp = (gchar *)xmlGetProp(scan, (const guchar *)"stock");
+ if (tmp) {
+ stock = g_strdup(tmp);
+ b->stock = stock;
+ xmlFree(tmp);
+ }
+ tmp = (gchar *)xmlGetProp(scan, (const guchar *)"label");
+ if (tmp) {
+ label = g_strdup(dgettext(table->translation_domain, tmp));
+ b->label = label;
+ xmlFree(tmp);
+ }
+ tmp = (gchar *)xmlGetProp(scan, (const guchar *)"response");
+ if (tmp) {
+ b->response = map_response(tmp);
+ xmlFree(tmp);
+ }
+
+ if (stock == NULL && label == NULL) {
+ g_warning("Error file '%s': missing button details in error '%s'", path, e->id);
+ g_free(stock);
+ g_free(label);
+ g_free(b);
+ } else {
+ lastbutton->next = b;
+ lastbutton = b;
+ }
+ }
+ }
+
+ g_hash_table_insert(table->alerts, (gpointer) e->id, e);
+ }
+ }
+
+ xmlFreeDoc(doc);
+}
+
+static void
+ee_load_tables(void)
+{
+ GDir *dir;
+ const gchar *d;
+ gchar *base;
+ struct _e_alert_table *table;
+ gint i;
+
+ if (alert_table != NULL)
+ return;
+
+ alert_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* setup system alert types */
+ table = g_malloc0(sizeof(*table));
+ table->domain = "builtin";
+ table->alerts = g_hash_table_new(g_str_hash, g_str_equal);
+ for (i = 0; i < G_N_ELEMENTS (default_alerts); i++)
+ g_hash_table_insert(table->alerts, (gpointer) default_alerts[i].id, &default_alerts[i]);
+ g_hash_table_insert(alert_table, (gpointer) table->domain, table);
+
+ /* look for installed alert tables */
+ base = g_build_filename (EVOLUTION_PRIVDATADIR, "errors", NULL);
+ dir = g_dir_open(base, 0, NULL);
+ if (dir == NULL) {
+ g_free (base);
+ return;
+ }
+
+ while ( (d = g_dir_read_name(dir)) ) {
+ gchar *path;
+
+ if (d[0] == '.')
+ continue;
+
+ path = g_build_filename(base, d, NULL);
+ ee_load(path);
+ g_free(path);
+ }
+
+ g_dir_close(dir);
+ g_free (base);
+}
+
+/* unfortunately, gmarkup_escape doesn't expose its gstring based api :( */
+static void
+ee_append_text(GString *out, const gchar *text)
+{
+ gchar 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 gchar *fmt, GPtrArray *args,
+ gboolean escape_args)
+{
+ const gchar *end, *newstart;
+ gint 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) {
+ if (escape_args)
+ ee_append_text(out, args->pdata[id]);
+ else
+ 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);
+}
+
+static void
+ee_response(GtkWidget *w, guint button, struct _e_alert *e)
+{
+ if (button == GTK_RESPONSE_HELP) {
+ g_signal_stop_emission_by_name(w, "response");
+ e_display_help (GTK_WINDOW (w), e->help_uri);
+ }
+}
+
+EAlert *
+e_alert_newv(const gchar *tag, const gchar *arg0, va_list ap)
+{
+ gchar *tmp;
+ GPtrArray *args;
+ EAlert *err = g_slice_new0 (EAlert);
+ err->tag = g_strdup (tag);
+ err->args = g_ptr_array_new_with_free_func (g_free);
+
+ tmp = (gchar *)arg0;
+ while (tmp) {
+ g_ptr_array_add(args, g_strdup (tmp));
+ tmp = va_arg(ap, gchar *);
+ }
+
+ return err;
+}
+
+/**
+ * e_alert_new:
+ * @tag: alert identifier
+ * @arg0: The first argument for the alert formatter. The list must
+ * be NULL terminated.
+ *
+ * Creates a new EAlert. The @tag argument is used to determine
+ * which alert to use, it is in the format domain:alert-id. The NULL
+ * terminated list of arguments, starting with @arg0 is used to fill
+ * out the alert definition.
+ **/
+EAlert *
+e_alert_new(const gchar *tag, const gchar *arg0, ...)
+{
+ EAlert *e;
+ va_list ap;
+
+ va_start(ap, arg0);
+ e = e_alert_newv(tag, arg0, ap);
+ va_end(ap);
+
+ return e;
+}
+
+GtkWidget *
+e_alert_new_dialog(GtkWindow *parent, EAlert *alert)
+{
+ struct _e_alert_table *table;
+ struct _e_alert *e;
+ struct _e_alert_button *b;
+ GtkWidget *hbox, *w, *scroll=NULL;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+ gchar *tmp, *domain, *id;
+ GString *out, *oerr;
+ GtkDialog *dialog;
+ gchar *str, *perr=NULL, *serr=NULL;
+
+ g_return_val_if_fail (alert != NULL, NULL);
+
+ if (alert_table == NULL)
+ ee_load_tables();
+
+ dialog = (GtkDialog *)gtk_dialog_new();
+ action_area = gtk_dialog_get_action_area (dialog);
+ content_area = gtk_dialog_get_content_area (dialog);
+
+ gtk_dialog_set_has_separator(dialog, FALSE);
+
+ gtk_widget_ensure_style ((GtkWidget *)dialog);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ if (parent)
+ gtk_window_set_transient_for ((GtkWindow *)dialog, parent);
+ else
+ g_warning (
+ "Something called %s() with a NULL parent window. "
+ "This is no longer legal, please fix it.", G_STRFUNC);
+
+ domain = alloca(strlen(alert->tag)+1);
+ strcpy(domain, alert->tag);
+ id = strchr(domain, ':');
+ if (id)
+ *id++ = 0;
+
+ if ( id == NULL
+ || (table = g_hash_table_lookup(alert_table, domain)) == NULL
+ || (e = g_hash_table_lookup(table->alerts, id)) == NULL) {
+ /* setup a dummy alert */
+ str = g_strdup_printf(_("Internal error, unknown error '%s' requested"), alert->tag);
+ tmp = g_strdup_printf("<span weight=\"bold\">%s</span>", str);
+ g_free(str);
+ 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_UNSET_FLAGS (w, GTK_CAN_FOCUS);
+ gtk_widget_show(w);
+ gtk_box_pack_start (GTK_BOX (content_area), 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, FALSE, FALSE, 12);
+
+ out = g_string_new("");
+
+ if (e->title && *e->title) {
+ ee_build_label(out, e->title, alert->args, FALSE);
+ gtk_window_set_title((GtkWindow *)dialog, out->str);
+ g_string_truncate(out, 0);
+ } else
+ gtk_window_set_title((GtkWindow *)dialog, out->str);
+
+ if (e->primary) {
+ g_string_append(out, "<span weight=\"bold\" size=\"larger\">");
+ ee_build_label(out, e->primary, alert->args, TRUE);
+ g_string_append(out, "</span>\n\n");
+ oerr = g_string_new("");
+ ee_build_label(oerr, e->primary, alert->args, FALSE);
+ perr = g_strdup (oerr->str);
+ g_string_free (oerr, TRUE);
+ } else
+ perr = g_strdup (gtk_window_get_title (GTK_WINDOW (dialog)));
+
+ if (e->secondary) {
+ ee_build_label(out, e->secondary, alert->args, TRUE);
+ oerr = g_string_new("");
+ ee_build_label(oerr, e->secondary, alert->args, TRUE);
+ serr = g_strdup (oerr->str);
+ g_string_free (oerr, TRUE);
+ }
+
+ if (e->scroll) {
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy ((GtkScrolledWindow *)scroll, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ }
+ 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);
+ GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_FOCUS);
+ g_string_free(out, TRUE);
+ if (e->scroll) {
+ gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow *)scroll, w);
+ gtk_box_pack_start((GtkBox *)hbox, scroll, FALSE, FALSE, 0);
+ gtk_window_set_default_size ((GtkWindow *)dialog, 360, 180);
+ } else
+ gtk_box_pack_start((GtkBox *)hbox, w, TRUE, TRUE, 0);
+
+ gtk_widget_show_all(hbox);
+
+ gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+ g_object_set_data_full ((GObject *) dialog, "primary", perr, g_free);
+ g_object_set_data_full ((GObject *) dialog, "secondary", serr, g_free);
+
+ return (GtkWidget *)dialog;
+}
+
+gint
+e_alert_run_dialog(GtkWindow *parent, EAlert *alert)
+{
+ GtkWidget *w;
+ gint res;
+
+ w = e_alert_new_dialog(parent, alert);
+
+ res = gtk_dialog_run((GtkDialog *)w);
+ gtk_widget_destroy(w);
+
+ return res;
+}
+
+/**
+ * e_alert_dialog_count_buttons:
+ * @dialog: a #GtkDialog
+ *
+ * Counts the number of buttons in @dialog's action area.
+ *
+ * Returns: number of action area buttons
+ **/
+guint
+e_alert_dialog_count_buttons (GtkDialog *dialog)
+{
+ GtkWidget *container;
+ GList *children, *iter;
+ guint n_buttons = 0;
+
+ g_return_val_if_fail (GTK_DIALOG (dialog), 0);
+
+ container = gtk_dialog_get_action_area (dialog);
+ children = gtk_container_get_children (GTK_CONTAINER (container));
+
+ /* Iterate over the children looking for buttons. */
+ for (iter = children; iter != NULL; iter = iter->next)
+ if (GTK_IS_BUTTON (iter->data))
+ n_buttons++;
+
+ g_list_free (children);
+
+ return n_buttons;
+}
+
+GtkWidget *
+e_alert_new_dialog_for_args (GtkWindow *parent, const gchar *tag, const gchar *arg0, ...)
+{
+ GtkWidget *w;
+ EAlert *e;
+ va_list ap;
+
+ va_start(ap, arg0);
+ e = e_alert_newv(tag, arg0, ap);
+ va_end(ap);
+
+ w = e_alert_new_dialog (parent, e);
+ e_alert_free (e);
+
+ return w;
+}
+
+gint
+e_alert_run_dialog_for_args (GtkWindow *parent, const gchar *tag, const gchar *arg0, ...)
+{
+ EAlert *e;
+ va_list ap;
+ gint response;
+
+ va_start(ap, arg0);
+ e = e_alert_newv(tag, arg0, ap);
+ va_end(ap);
+
+ response = e_alert_run_dialog (parent, e);
+ e_alert_free (e);
+
+ return response;
+}