aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-alert.c
diff options
context:
space:
mode:
authorJonathon Jongsma <jonathon@quotidian.org>2009-12-01 01:34:43 +0800
committerJonathon Jongsma <jonathon@quotidian.org>2009-12-01 03:33:04 +0800
commitc2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc (patch)
treee6430bf480afc3e4a220fdf713413c8df4a9da41 /e-util/e-alert.c
parent495e9bf8001e2209a35e8991c07ec038576efdd4 (diff)
downloadgsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar.gz
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar.bz2
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar.lz
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar.xz
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.tar.zst
gsoc2013-evolution-c2ee0afbc85ce3ddf38ec07497c0cdae4f42a7fc.zip
Rename EError to EAlert to match general use better
The EError mechanism is used both for error dialogs as well as basic alerts or user prompts, so we should give it a more general name which matches this use. This patch also cleans up a few includes of e-alert.h (formerly e-error.h) that were not actually being used. https://bugzilla.gnome.org/show_bug.cgi?id=602963
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;
+}