diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-09-30 09:11:44 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-09-30 09:11:44 +0800 |
commit | 0e4c54eddced72c9639001849148fe1813c5dc4e (patch) | |
tree | 3ba53e853901d3b7e733b6aed145c5eb54e6a2fc /e-util | |
parent | d3b09614221d075452496a5198a3910d07eb2818 (diff) | |
download | gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar.gz gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar.bz2 gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar.lz gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar.xz gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.tar.zst gsoc2013-evolution-0e4c54eddced72c9639001849148fe1813c5dc4e.zip |
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.
Diffstat (limited to 'e-util')
-rw-r--r-- | e-util/Makefile.am | 2 | ||||
-rw-r--r-- | e-util/e-alert-activity.c | 17 | ||||
-rw-r--r-- | e-util/e-alert-dialog.c | 107 | ||||
-rw-r--r-- | e-util/e-alert-sink.c | 92 | ||||
-rw-r--r-- | e-util/e-alert-sink.h | 62 | ||||
-rw-r--r-- | e-util/e-alert.c | 595 | ||||
-rw-r--r-- | e-util/e-alert.h | 31 |
7 files changed, 592 insertions, 314 deletions
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, - "<span weight=\"bold\" size=\"larger\">%s</span>\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 <http://www.gnu.org/licenses/> + * + */ + +/** + * 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_ALERT_SINK_H +#define E_ALERT_SINK_H + +#include <gtk/gtk.h> +#include <e-util/e-alert.h> + +/* 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: <error id="error-id" type="info|warning|question|error"? - response="default_response"? modal="true"? > - <title>Window Title</title>? + response="default_response"? > <primary>Primary error text.</primary>? <secondary>Secondary error text.</secondary>? <button stock="stock-button-id"? label="button label"? @@ -225,20 +215,12 @@ e_alert_load (const gchar *path) 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); + e->message_type = map_type (tmp); if (tmp) xmlFree (tmp); @@ -248,27 +230,15 @@ e_alert_load (const gchar *path) 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)); + e->primary_text = 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)); + e->secondary_text = g_strdup (dgettext (table->translation_domain, tmp)); xmlFree (tmp); } } else if (!strcmp((gchar *)scan->name, "button")) { @@ -361,120 +331,232 @@ e_alert_load_tables (void) g_free (base); } +static gchar * +alert_format_string (const gchar *format, + GPtrArray *args) +{ + GString *string; + const gchar *end, *newstart; + gint id; + + string = g_string_sized_new (strlen (format)); + + while (format + && (newstart = strchr (format, '{')) + && (end = strchr (newstart+1, '}'))) { + g_string_append_len (string, format, newstart - format); + id = atoi (newstart + 1); + if (id < args->len) { + g_string_append (string, args->pdata[id]); + } else + g_warning("Error references argument %d not supplied by caller", id); + format = end + 1; + } + + g_string_append (string, format); + + return g_string_free (string, FALSE); +} + static void -e_alert_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +alert_set_tag (EAlert *alert, + const gchar *tag) { - EAlert *alert = (EAlert*) object; + struct _e_alert *definition; + struct _e_alert_table *table; + gchar *domain, *id; - switch (property_id) - { - case PROP_TAG: - g_value_set_string (value, alert->priv->tag); - break; - case PROP_ARGS: - g_value_set_boxed (value, alert->priv->args); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + alert->priv->tag = g_strdup (tag); + + g_return_if_fail (alert_table); + + domain = g_alloca (strlen (tag) + 1); + strcpy (domain, tag); + id = strchr (domain, ':'); + if (id) + *id++ = 0; + else { + g_warning ("Alert tag '%s' is missing a domain", tag); + return; } + + table = g_hash_table_lookup (alert_table, domain); + g_return_if_fail (table); + + definition = g_hash_table_lookup (table->alerts, id); + g_warn_if_fail (definition); + + alert->priv->definition = definition; + e_alert_set_message_type (alert, definition->message_type); + e_alert_set_default_response (alert, definition->default_response); } static void -e_alert_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) +alert_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { EAlert *alert = (EAlert*) object; - switch (property_id) - { + switch (property_id) { case PROP_TAG: - alert->priv->tag = g_value_dup_string (value); - break; + alert_set_tag ( + E_ALERT (object), + g_value_get_string (value)); + return; + case PROP_ARGS: alert->priv->args = g_value_dup_boxed (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + + case PROP_MESSAGE_TYPE: + e_alert_set_message_type ( + E_ALERT (object), + g_value_get_enum (value)); + return; + + case PROP_PRIMARY_TEXT: + e_alert_set_primary_text ( + E_ALERT (object), + g_value_get_string (value)); + return; + + case PROP_SECONDARY_TEXT: + e_alert_set_secondary_text ( + E_ALERT (object), + g_value_get_string (value)); + return; } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -e_alert_dispose (GObject *object) +alert_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { EAlert *alert = (EAlert*) object; - if (alert->priv->tag) { - g_free (alert->priv->tag); - alert->priv->tag = NULL; - } + switch (property_id) { + case PROP_TAG: + g_value_set_string (value, alert->priv->tag); + return; - if (alert->priv->args) { - /* arg strings will be freed automatically since we set a free func when - * creating the ptr array */ - g_boxed_free (G_TYPE_PTR_ARRAY, alert->priv->args); - alert->priv->args = NULL; + case PROP_ARGS: + g_value_set_boxed (value, alert->priv->args); + return; + + case PROP_MESSAGE_TYPE: + g_value_set_enum ( + value, e_alert_get_message_type ( + E_ALERT (object))); + return; + + case PROP_PRIMARY_TEXT: + g_value_set_string ( + value, e_alert_get_primary_text ( + E_ALERT (object))); + return; + + case PROP_SECONDARY_TEXT: + g_value_set_string ( + value, e_alert_get_secondary_text ( + E_ALERT (object))); + return; } - G_OBJECT_CLASS (e_alert_parent_class)->dispose (object); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -e_alert_constructed (GObject *obj) +alert_finalize (GObject *object) { - EAlert *alert = E_ALERT (obj); - - struct _e_alert_table *table; - gchar *domain, *id; - - g_return_if_fail (alert_table); - g_return_if_fail (alert->priv->tag); + EAlertPrivate *priv; - domain = g_alloca (strlen (alert->priv->tag)+1); - strcpy (domain, alert->priv->tag); - id = strchr (domain, ':'); - if (id) - *id++ = 0; - - table = g_hash_table_lookup (alert_table, domain); - g_return_if_fail (table); + priv = E_ALERT_GET_PRIVATE (object); - alert->priv->definition = g_hash_table_lookup (table->alerts, id); + g_free (priv->tag); + g_free (priv->primary_text); + g_free (priv->secondary_text); - g_warn_if_fail (alert->priv->definition); + g_ptr_array_free (priv->args, TRUE); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_alert_parent_class)->finalize (object); } + static void -e_alert_class_init (EAlertClass *klass) +e_alert_class_init (EAlertClass *class) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (EAlertPrivate)); - - object_class->get_property = e_alert_get_property; - object_class->set_property = e_alert_set_property; - object_class->dispose = e_alert_dispose; - object_class->constructed = e_alert_constructed; - - g_object_class_install_property (object_class, - PROP_TAG, - g_param_spec_string ("tag", - "alert tag", - "A tag describing the alert", - "", - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_ARGS, - g_param_spec_boxed ("args", - "Arguments", - "Arguments for formatting the alert", - G_TYPE_PTR_ARRAY, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (EAlertPrivate)); + + object_class->set_property = alert_set_property; + object_class->get_property = alert_get_property; + object_class->finalize = alert_finalize; + + g_object_class_install_property ( + object_class, + PROP_ARGS, + g_param_spec_boxed ( + "args", + "Arguments", + "Arguments for formatting the alert", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_TAG, + g_param_spec_string ( + "tag", + "alert tag", + "A tag describing the alert", + "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MESSAGE_TYPE, + g_param_spec_enum ( + "message-type", + NULL, + NULL, + GTK_TYPE_MESSAGE_TYPE, + GTK_MESSAGE_ERROR, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_PRIMARY_TEXT, + g_param_spec_string ( + "primary-text", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_SECONDARY_TEXT, + g_param_spec_string ( + "secondary-text", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); e_alert_load_tables (); } @@ -502,17 +584,17 @@ EAlert * e_alert_new (const gchar *tag, ...) { EAlert *e; - va_list ap; + va_list va; - va_start (ap, tag); - e = e_alert_new_valist (tag, ap); - va_end (ap); + va_start (va, tag); + e = e_alert_new_valist (tag, va); + va_end (va); return e; } EAlert * -e_alert_new_valist (const gchar *tag, va_list ap) +e_alert_new_valist (const gchar *tag, va_list va) { EAlert *alert; GPtrArray *args; @@ -520,10 +602,10 @@ e_alert_new_valist (const gchar *tag, va_list ap) args = g_ptr_array_new_with_free_func (g_free); - tmp = va_arg (ap, gchar *); + tmp = va_arg (va, gchar *); while (tmp) { g_ptr_array_add (args, g_strdup (tmp)); - tmp = va_arg (ap, gchar *); + tmp = va_arg (va, gchar *); } alert = e_alert_new_array (tag, args); @@ -539,138 +621,114 @@ e_alert_new_array (const gchar *tag, GPtrArray *args) return g_object_new (E_TYPE_ALERT, "tag", tag, "args", args, NULL); } -/* unfortunately, gmarkup_escape doesn't expose its gstring based api :( */ -static void -e_alert_append_text_escaped (GString *out, const gchar *text) +gint +e_alert_get_default_response (EAlert *alert) { - gchar *markup; + g_return_val_if_fail (E_IS_ALERT (alert), 0); - markup = g_markup_escape_text (text, -1); - g_string_append (out, markup); - g_free (markup); + return alert->priv->default_response; } -static void -e_alert_format_string (GString *out, - const gchar *fmt, - GPtrArray *args, - gboolean escape_args) +void +e_alert_set_default_response (EAlert *alert, + gint response_id) { - const gchar *end, *newstart; - gint id; + g_return_if_fail (E_IS_ALERT (alert)); - 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) - e_alert_append_text_escaped (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); + alert->priv->default_response = response_id; } -guint32 -e_alert_get_flags (EAlert *alert) +GtkMessageType +e_alert_get_message_type (EAlert *alert) { - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, 0); - return alert->priv->definition->flags; -} + g_return_val_if_fail (E_IS_ALERT (alert), GTK_MESSAGE_OTHER); -const gchar * -e_alert_peek_stock_image (EAlert *alert) -{ - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL); - return type_map[alert->priv->definition->type].icon; + return alert->priv->message_type; } -gint -e_alert_get_default_response (EAlert *alert) +void +e_alert_set_message_type (EAlert *alert, + GtkMessageType message_type) { - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, 0); - return alert->priv->definition->default_response; + g_return_if_fail (E_IS_ALERT (alert)); + + alert->priv->message_type = message_type; + + g_object_notify (G_OBJECT (alert), "message-type"); } -gchar * -e_alert_get_title (EAlert *alert, - gboolean escaped) +const gchar * +e_alert_get_primary_text (EAlert *alert) { - GString *formatted; + g_return_val_if_fail (E_IS_ALERT (alert), NULL); - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL); - - formatted = g_string_new (""); + if (alert->priv->primary_text != NULL) + goto exit; - if (alert->priv->definition->title != NULL) - e_alert_format_string ( - formatted, alert->priv->definition->title, - alert->priv->args, escaped); + if (alert->priv->definition == NULL) + goto exit; - return g_string_free (formatted, FALSE); -} + if (alert->priv->definition->primary_text == NULL) + goto exit; -gchar * -e_alert_get_primary_text (EAlert *alert, - gboolean escaped) -{ - GString *formatted; + if (alert->priv->args == NULL) + goto exit; - g_return_val_if_fail (alert && alert->priv, NULL); + alert->priv->primary_text = alert_format_string ( + alert->priv->definition->primary_text, + alert->priv->args); - formatted = g_string_new (""); +exit: + return alert->priv->primary_text; +} - if (alert->priv->definition != NULL) - if (alert->priv->definition->primary != NULL) { - e_alert_format_string ( - formatted, alert->priv->definition->primary, - alert->priv->args, escaped); - } else { - gchar *title; +void +e_alert_set_primary_text (EAlert *alert, + const gchar *primary_text) +{ + g_return_if_fail (E_IS_ALERT (alert)); - title = e_alert_get_title (alert, escaped); - g_string_append (formatted, title); - g_free (title); - } - else { - g_string_append_printf ( - formatted, - _("Internal error, unknown error '%s' requested"), - alert->priv->tag); - } + g_free (alert->priv->primary_text); + alert->priv->primary_text = g_strdup (primary_text); - return g_string_free (formatted, FALSE); + g_object_notify (G_OBJECT (alert), "primary-text"); } -gchar * -e_alert_get_secondary_text (EAlert *alert, - gboolean escaped) +const gchar * +e_alert_get_secondary_text (EAlert *alert) { - GString *formatted; + g_return_val_if_fail (E_IS_ALERT (alert), NULL); - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL); + if (alert->priv->secondary_text != NULL) + goto exit; + + if (alert->priv->definition == NULL) + goto exit; + + if (alert->priv->definition->secondary_text == NULL) + goto exit; - formatted = g_string_new (""); + if (alert->priv->args == NULL) + goto exit; - if (alert->priv->definition->secondary != NULL) - e_alert_format_string ( - formatted, alert->priv->definition->secondary, - alert->priv->args, escaped); + alert->priv->secondary_text = alert_format_string ( + alert->priv->definition->secondary_text, + alert->priv->args); - return g_string_free (formatted, FALSE); +exit: + return alert->priv->secondary_text; } -gboolean -e_alert_get_scroll (EAlert *alert) +void +e_alert_set_secondary_text (EAlert *alert, + const gchar *secondary_text) { - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, FALSE); - return alert->priv->definition->scroll; + g_return_if_fail (E_IS_ALERT (alert)); + + g_free (alert->priv->secondary_text); + alert->priv->secondary_text = g_strdup (secondary_text); + + g_object_notify (G_OBJECT (alert), "secondary-text"); } struct _e_alert_button * @@ -679,3 +737,60 @@ e_alert_peek_buttons (EAlert *alert) g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL); return alert->priv->definition->buttons; } + +GtkWidget * +e_alert_create_image (EAlert *alert, + GtkIconSize size) +{ + const gchar *stock_id; + + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + switch (e_alert_get_message_type (alert)) { + case GTK_MESSAGE_INFO: + stock_id = GTK_STOCK_DIALOG_INFO; + break; + case GTK_MESSAGE_WARNING: + stock_id = GTK_STOCK_DIALOG_WARNING; + break; + case GTK_MESSAGE_QUESTION: + stock_id = GTK_STOCK_DIALOG_QUESTION; + break; + case GTK_MESSAGE_ERROR: + stock_id = GTK_STOCK_DIALOG_ERROR; + break; + default: + stock_id = GTK_STOCK_MISSING_IMAGE; + g_warn_if_reached (); + break; + } + + return gtk_image_new_from_stock (stock_id, size); +} + +void +e_alert_submit (GtkWidget *widget, + const gchar *tag, + ...) +{ + va_list va; + + va_start (va, tag); + e_alert_submit_valist (widget, tag, va); + va_end (va); +} + +void +e_alert_submit_valist (GtkWidget *widget, + const gchar *tag, + va_list va) +{ + EAlert *alert; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (tag != NULL); + + alert = e_alert_new_valist (tag, va); + e_alert_sink_submit_alert (widget, alert); + g_object_unref (alert); +} diff --git a/e-util/e-alert.h b/e-util/e-alert.h index 4c515b53f4..7b07970361 100644 --- a/e-util/e-alert.h +++ b/e-util/e-alert.h @@ -81,21 +81,32 @@ GType e_alert_get_type (void); EAlert * e_alert_new (const gchar *tag, ...) G_GNUC_NULL_TERMINATED; EAlert * e_alert_new_valist (const gchar *tag, - va_list ap); + va_list va); EAlert * e_alert_new_array (const gchar *tag, GPtrArray *args); -guint32 e_alert_get_flags (EAlert *alert); -const gchar * e_alert_peek_stock_image (EAlert *alert); gint e_alert_get_default_response (EAlert *alert); -gchar * e_alert_get_title (EAlert *alert, - gboolean escaped); -gchar * e_alert_get_primary_text (EAlert *alert, - gboolean escaped); -gchar * e_alert_get_secondary_text (EAlert *alert, - gboolean escaped); -gboolean e_alert_get_scroll (EAlert *alert); +void e_alert_set_default_response (EAlert *alert, + gint response_id); +GtkMessageType e_alert_get_message_type (EAlert *alert); +void e_alert_set_message_type (EAlert *alert, + GtkMessageType message_type); +const gchar * e_alert_get_primary_text (EAlert *alert); +void e_alert_set_primary_text (EAlert *alert, + const gchar *primary_text); +const gchar * e_alert_get_secondary_text (EAlert *alert); +void e_alert_set_secondary_text (EAlert *alert, + const gchar *secondary_text); struct _e_alert_button * e_alert_peek_buttons (EAlert *alert); +GtkWidget * e_alert_create_image (EAlert *alert, + GtkIconSize size); + +void e_alert_submit (GtkWidget *widget, + const gchar *tag, + ...) G_GNUC_NULL_TERMINATED; +void e_alert_submit_valist (GtkWidget *widget, + const gchar *tag, + va_list va); G_END_DECLS |