From 0e4c54eddced72c9639001849148fe1813c5dc4e Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 29 Sep 2010 21:11:44 -0400 Subject: Messin around with EAlerts. Trying out a new interface called EAlertSink. The idea is to centralize how errors are shown to the user. A GtkWindow subclass would implement the EAlertSink interface, which consists of a single method: void (*submit_alert) (EAlertSink *alert_sink, EAlert *alert); The subclass has complete control over what to do with the EAlert, although I imagine we'll wind up implementing various alert-handling policies as standalone widgets such as EAlertDialog. I'd like to try an EAlertInfoBar. Code that would otherwise display an error dialog itself would instead pass the EAlert to an appropriate EAlertSink and be done with it. Nothing is final yet. Still hacking on EAlert trying to find an API that feels right for these use cases. --- calendar/gui/dialogs/comp-editor.c | 15 +- composer/e-msg-composer.c | 2 + doc/reference/shell/eshell-sections.txt | 6 - doc/reference/shell/tmpl/e-alert.sgml | 42 -- doc/reference/shell/tmpl/eshell-unused.sgml | 36 ++ e-util/Makefile.am | 2 + e-util/e-alert-activity.c | 17 +- e-util/e-alert-dialog.c | 107 +++-- e-util/e-alert-sink.c | 92 +++++ e-util/e-alert-sink.h | 62 +++ e-util/e-alert.c | 595 +++++++++++++++++----------- e-util/e-alert.h | 31 +- widgets/misc/e-signature-editor.c | 18 +- 13 files changed, 648 insertions(+), 377 deletions(-) create mode 100644 e-util/e-alert-sink.c create mode 100644 e-util/e-alert-sink.h diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c index 2213d478bc..63196abea4 100644 --- a/calendar/gui/dialogs/comp-editor.c +++ b/calendar/gui/dialogs/comp-editor.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -193,7 +194,9 @@ static void page_dates_changed_cb (CompEditor *editor, static void obj_modified_cb (ECal *client, GList *objs, CompEditor *editor); static void obj_removed_cb (ECal *client, GList *uids, CompEditor *editor); -G_DEFINE_TYPE (CompEditor, comp_editor, GTK_TYPE_WINDOW) +G_DEFINE_TYPE_WITH_CODE ( + CompEditor, comp_editor, GTK_TYPE_WINDOW, + G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL)) enum { OBJECT_CREATED, @@ -831,9 +834,8 @@ action_save_cb (GtkAction *action, } if (!e_cal_is_read_only (priv->client, &read_only, NULL) || read_only) { - e_alert_run_dialog_for_args ( - (GtkWindow *) gtk_widget_get_toplevel ( - GTK_WIDGET (editor)), + e_alert_submit ( + GTK_WIDGET (editor), "calendar:prompt-read-only-cal-editor", e_source_peek_name ( e_cal_get_source (priv->client)), @@ -1878,9 +1880,8 @@ prompt_and_save_changes (CompEditor *editor, gboolean send) switch (save_component_dialog (GTK_WINDOW (editor), priv->comp)) { case GTK_RESPONSE_YES: /* Save */ if (!e_cal_is_read_only (priv->client, &read_only, NULL) || read_only) { - e_alert_run_dialog_for_args ( - (GtkWindow *) gtk_widget_get_toplevel ( - GTK_WIDGET (editor)), + e_alert_submit ( + GTK_WIDGET (editor), "calendar:prompt-read-only-cal-editor", e_source_peek_name ( e_cal_get_source (priv->client)), diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index f5f47782f5..d4e45d4d15 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -47,6 +47,7 @@ #include "e-util/e-account-utils.h" #include "e-util/e-alert-dialog.h" +#include "e-util/e-alert-sink.h" #include "e-util/e-dialog-utils.h" #include "e-util/e-extensible.h" #include "e-util/e-plugin-ui.h" @@ -124,6 +125,7 @@ G_DEFINE_TYPE_WITH_CODE ( EMsgComposer, e_msg_composer, GTKHTML_TYPE_EDITOR, + G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL) G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) /** diff --git a/doc/reference/shell/eshell-sections.txt b/doc/reference/shell/eshell-sections.txt index d8b8a5cbd5..3dc6eb1bc4 100644 --- a/doc/reference/shell/eshell-sections.txt +++ b/doc/reference/shell/eshell-sections.txt @@ -511,12 +511,6 @@ EActivityProxyPrivate
e-alert User Alert Handling -E_ALERT_INFO -E_ALERT_INFO_PRIMARY -E_ALERT_WARNING -E_ALERT_WARNING_PRIMARY -E_ALERT_ERROR -E_ALERT_ERROR_PRIMARY E_ALERT_ASK_FILE_EXISTS_OVERWRITE E_ALERT_NO_SAVE_FILE E_ALERT_NO_LOAD_FILE diff --git a/doc/reference/shell/tmpl/e-alert.sgml b/doc/reference/shell/tmpl/e-alert.sgml index 3666408f7b..46df813c66 100644 --- a/doc/reference/shell/tmpl/e-alert.sgml +++ b/doc/reference/shell/tmpl/e-alert.sgml @@ -20,48 +20,6 @@ User Alert Handling - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml index 59aed5d191..1488652840 100644 --- a/doc/reference/shell/tmpl/eshell-unused.sgml +++ b/doc/reference/shell/tmpl/eshell-unused.sgml @@ -88,6 +88,42 @@ e-shell-window.sgml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e-util/Makefile.am b/e-util/Makefile.am index f1c6131965..4b08698896 100644 --- a/e-util/Makefile.am +++ b/e-util/Makefile.am @@ -14,6 +14,7 @@ eutilinclude_HEADERS = \ e-alert.h \ e-alert-activity.h \ e-alert-dialog.h \ + e-alert-sink.h \ e-binding.h \ e-bit-array.h \ e-categories-config.h \ @@ -93,6 +94,7 @@ libeutil_la_SOURCES = \ e-alert.c \ e-alert-activity.c \ e-alert-dialog.c \ + e-alert-sink.c \ e-binding.c \ e-bit-array.c \ e-categories-config.c \ diff --git a/e-util/e-alert-activity.c b/e-util/e-alert-activity.c index 8e6a6f78e5..3a97a4eab4 100644 --- a/e-util/e-alert-activity.c +++ b/e-util/e-alert-activity.c @@ -109,23 +109,22 @@ alert_activity_constructed (GObject *object) EAlertActivity *alert_activity; EAlert *alert; GtkWidget *message_dialog; - gchar *primary_text; - gchar *secondary_text; + const gchar *primary_text; + const gchar *secondary_text; + activity = E_ACTIVITY (object); alert_activity = E_ALERT_ACTIVITY (object); - message_dialog = e_alert_activity_get_message_dialog (alert_activity); + message_dialog = e_alert_activity_get_message_dialog (alert_activity); alert = e_alert_dialog_get_alert (E_ALERT_DIALOG (message_dialog)); - primary_text = e_alert_get_primary_text (alert, FALSE); - secondary_text = e_alert_get_secondary_text (alert, FALSE); - g_object_unref (alert); - activity = E_ACTIVITY (alert_activity); + primary_text = e_alert_get_primary_text (alert); e_activity_set_primary_text (activity, primary_text); + + secondary_text = e_alert_get_secondary_text (alert); e_activity_set_secondary_text (activity, secondary_text); - g_free (primary_text); - g_free (secondary_text); + g_object_unref (alert); /* This is a constructor property, so can't do it in init(). * XXX What we really want to do is override the property's diff --git a/e-util/e-alert-dialog.c b/e-util/e-alert-dialog.c index 1aa238cdcc..0057ed91aa 100644 --- a/e-util/e-alert-dialog.c +++ b/e-util/e-alert-dialog.c @@ -113,17 +113,21 @@ e_alert_dialog_constructed (GObject *obj) EAlertDialog *self = (EAlertDialog*) obj; EAlert *alert; struct _e_alert_button *b; - GtkWidget *hbox, *w, *scroll=NULL; GtkWidget *action_area; GtkWidget *content_area; - GString *out; - gchar *title, *primary, *secondary; + GtkWidget *container; + GtkWidget *widget; + PangoAttribute *attr; + PangoAttrList *list; + const gchar *primary, *secondary; g_return_if_fail (self != NULL); self->priv = ALERT_DIALOG_PRIVATE (self); alert = self->priv->alert; + gtk_window_set_title (GTK_WINDOW (self), " "); + action_area = gtk_dialog_get_action_area ((GtkDialog*) self); content_area = gtk_dialog_get_content_area ((GtkDialog*) self); @@ -142,8 +146,6 @@ e_alert_dialog_constructed (GObject *obj) "Something called %s() with a NULL parent window. " "This is no longer legal, please fix it.", G_STRFUNC); - if (e_alert_get_flags (alert) & GTK_DIALOG_MODAL) - gtk_window_set_modal ((GtkWindow *)self, TRUE); gtk_window_set_destroy_with_parent ((GtkWindow *)self, TRUE); b = e_alert_peek_buttons (alert); @@ -174,56 +176,51 @@ e_alert_dialog_constructed (GObject *obj) gtk_dialog_set_default_response ((GtkDialog*) self, e_alert_get_default_response (alert)); - hbox = gtk_hbox_new (FALSE, 0); - gtk_container_set_border_width ((GtkContainer *)hbox, 12); - - w = gtk_image_new_from_stock - (e_alert_peek_stock_image (alert), GTK_ICON_SIZE_DIALOG); - gtk_misc_set_alignment ((GtkMisc *)w, 0.0, 0.0); - gtk_box_pack_start ((GtkBox *)hbox, w, FALSE, FALSE, 12); - - title = e_alert_get_title (alert, FALSE); - gtk_window_set_title ((GtkWindow *)self, title); - - out = g_string_new (""); - primary = e_alert_get_primary_text (alert, TRUE); - if (primary) { - g_string_append_printf (out, - "%s\n\n", - primary); - } - - secondary = e_alert_get_secondary_text (alert, TRUE); - if (secondary) { - g_string_append (out, secondary); - } - - g_free (secondary); - g_free (title); - g_free (primary); - - if (e_alert_get_scroll (alert)) { - scroll = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ( - GTK_SCROLLED_WINDOW (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_set_can_focus (w, FALSE); - g_string_free (out, TRUE); - if (e_alert_get_scroll (alert)) { - 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 *)self, 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); + widget = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (widget), 12); + gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = e_alert_create_image (alert, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + container = widget; + + primary = e_alert_get_primary_text (alert); + secondary = e_alert_get_secondary_text (alert); + + list = pango_attr_list_new (); + attr = pango_attr_scale_new (PANGO_SCALE_LARGE); + pango_attr_list_insert (list, attr); + attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); + pango_attr_list_insert (list, attr); + + widget = gtk_label_new (primary); + gtk_label_set_attributes (GTK_LABEL (widget), list); + gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); + gtk_label_set_selectable (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_set_can_focus (widget, FALSE); + gtk_widget_show (widget); + + widget = gtk_label_new (secondary); + gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); + gtk_label_set_selectable (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_set_can_focus (widget, FALSE); + gtk_widget_show (widget); + + pango_attr_list_unref (list); } static void diff --git a/e-util/e-alert-sink.c b/e-util/e-alert-sink.c new file mode 100644 index 0000000000..de676ea778 --- /dev/null +++ b/e-util/e-alert-sink.c @@ -0,0 +1,92 @@ +/* + * e-alert-sink.c + * + * 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 + * + */ + +/** + * SECTION: e-alert-sink + * @short_description: an interface to handle alerts + * @include: e-util/e-alert-sink.h + * + * A widget that implements #EAlertSink means it can handle #EAlerts, + * usually by displaying them to the user. + **/ + +#include "e-alert-sink.h" + +#include "e-alert-dialog.h" + +G_DEFINE_INTERFACE ( + EAlertSink, + e_alert_sink, + GTK_TYPE_WIDGET) + +static void +alert_sink_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + GtkWidget *dialog; + gpointer parent; + + /* This is just a lame fallback handler. Implementors + * are strongly encouraged to override this method. */ + + parent = gtk_widget_get_toplevel (GTK_WIDGET (alert_sink)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + dialog = e_alert_dialog_new (parent, alert); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +e_alert_sink_default_init (EAlertSinkInterface *interface) +{ + interface->submit_alert = alert_sink_submit_alert; +} + +/** + * e_alert_sink_submit_alert: + * @widget: a #GtkWidget, either itself an #EAlertSink or a child of one + * @alert: an #EAlert + * + * This function is a place to pass #EAlert objects. Beyond that it has no + * well-defined behavior. It's up to the widget implementing the #EAlertSink + * interface to decide what to do with them. + * + * Either @widget or one of its parents must implement #EAlertSink. + * + * The default behavior is to display the @alert in a dialog. + **/ +void +e_alert_sink_submit_alert (GtkWidget *widget, + EAlert *alert) +{ + EAlertSinkInterface *interface; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (E_IS_ALERT (alert)); + + while (widget != NULL && !E_IS_ALERT_SINK (widget)) + widget = gtk_widget_get_parent (widget); + + g_return_if_fail (E_IS_ALERT_SINK (widget)); + + interface = E_ALERT_SINK_GET_INTERFACE (widget); + g_return_if_fail (interface->submit_alert != NULL); + + interface->submit_alert (E_ALERT_SINK (widget), alert); +} diff --git a/e-util/e-alert-sink.h b/e-util/e-alert-sink.h new file mode 100644 index 0000000000..35c56f95b1 --- /dev/null +++ b/e-util/e-alert-sink.h @@ -0,0 +1,62 @@ +/* + * e-alert-sink.h + * + * 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 + * + */ + +#ifndef E_ALERT_SINK_H +#define E_ALERT_SINK_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_ALERT_SINK \ + (e_alert_sink_get_type ()) +#define E_ALERT_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ALERT_SINK, EAlertSink)) +#define E_ALERT_SINK_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ALERT_SINK, EAlertSinkInterface)) +#define E_IS_ALERT_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ALERT_SINK)) +#define E_IS_ALERT_SINK_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ALERT_SINK)) +#define E_ALERT_SINK_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_ALERT_SINK, EAlertSinkInterface)) + +G_BEGIN_DECLS + +typedef struct _EAlertSink EAlertSink; +typedef struct _EAlertSinkInterface EAlertSinkInterface; + +struct _EAlertSinkInterface { + GTypeInterface parent_interface; + + void (*submit_alert) (EAlertSink *alert_sink, + EAlert *alert); +}; + +GType e_alert_sink_get_type (void); +void e_alert_sink_submit_alert (GtkWidget *widget, + EAlert *alert); + +G_END_DECLS + +#endif /* E_ALERT_SINK_H */ diff --git a/e-util/e-alert.c b/e-util/e-alert.c index 49ba6af36b..65ab57bd8f 100644 --- a/e-util/e-alert.c +++ b/e-util/e-alert.c @@ -38,6 +38,7 @@ #include "e-util.h" #include "e-util-private.h" #include "e-alert.h" +#include "e-alert-sink.h" #define d(x) @@ -46,14 +47,11 @@ ((obj), E_TYPE_ALERT, EAlertPrivate)) struct _e_alert { - guint32 flags; const gchar *id; - gint type; + GtkMessageType message_type; gint default_response; - const gchar *title; - const gchar *primary; - const gchar *secondary; - gboolean scroll; + const gchar *primary_text; + const gchar *secondary_text; struct _e_alert_button *buttons; }; @@ -72,12 +70,10 @@ static struct _e_alert_button default_ok_button = { }; static struct _e_alert default_alerts[] = { - { GTK_DIALOG_MODAL, "error", 3, GTK_RESPONSE_OK, - N_("Evolution Error"), "{0}", "{1}", FALSE, - &default_ok_button }, - { GTK_DIALOG_MODAL, "warning", 1, GTK_RESPONSE_OK, - N_("Evolution Warning"), "{0}", "{1}", FALSE, - &default_ok_button } + { "error", GTK_MESSAGE_ERROR, GTK_RESPONSE_OK, + "{0}", "{1}", &default_ok_button }, + { "warning", GTK_MESSAGE_WARNING, GTK_RESPONSE_OK, + "{0}", "{1}", &default_ok_button } }; /* ********************************************************************** */ @@ -109,28 +105,16 @@ map_response (const gchar *name) 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) +static GtkMessageType +map_type (const gchar *nick) { - gint i; + GEnumClass *class; + GEnumValue *value; - if (name) { - for (i = 0; i < G_N_ELEMENTS (type_map); i++) - if (!strcmp (name, type_map[i].name)) - return i; - } + class = g_type_class_peek (GTK_TYPE_MESSAGE_TYPE); + value = g_enum_get_value_by_nick (class, nick); - return 3; + return (value != NULL) ? value->value : GTK_MESSAGE_ERROR; } G_DEFINE_TYPE ( @@ -140,22 +124,28 @@ G_DEFINE_TYPE ( enum { PROP_0, + PROP_ARGS, PROP_TAG, - PROP_ARGS + PROP_MESSAGE_TYPE, + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT }; struct _EAlertPrivate { gchar *tag; GPtrArray *args; + gchar *primary_text; + gchar *secondary_text; struct _e_alert *definition; + GtkMessageType message_type; + gint default_response; }; /* XML format: - Window Title? + response="default_response"? > Primary error text.? Secondary error text.?