aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-10-06 22:55:27 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-10-13 01:59:00 +0800
commit4118d671d44b71592f0e91abb63f2468baaa9318 (patch)
treee70f787f68034a16df1c59f75c8869618b02146b /widgets
parenta06e4484b8df804124b5bcf88d94dec5acfba270 (diff)
downloadgsoc2013-evolution-4118d671d44b71592f0e91abb63f2468baaa9318.tar.gz
gsoc2013-evolution-4118d671d44b71592f0e91abb63f2468baaa9318.tar.zst
gsoc2013-evolution-4118d671d44b71592f0e91abb63f2468baaa9318.zip
Composer: Show cancellable operations and errors inline.
'Send' and 'Save Draft' are now asynchronous and run outside of Evolution's MailMsg infrastructure. Add an EActivityBar to the composer window so these asynchronous operations can be tracked and cancelled even in the absense of a main window. Also add an EAlertBar to the composer window so error messages can be shown directly in the window. Instead of calling e_alert_dialog_run_for_args(), call e_alert_submit() and pass the EMsgComposer as the widget argument. The EMsgComposer will decide whether to show an EAlertDialog or use the EAlertBar, depending on the GtkMessageType of the alert.
Diffstat (limited to 'widgets')
-rw-r--r--widgets/misc/Makefile.am4
-rw-r--r--widgets/misc/e-activity-bar.c351
-rw-r--r--widgets/misc/e-activity-bar.h67
-rw-r--r--widgets/misc/e-activity-proxy.c7
-rw-r--r--widgets/misc/e-alert-bar.c235
-rw-r--r--widgets/misc/e-alert-bar.h66
6 files changed, 726 insertions, 4 deletions
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 21faf33517..4ee47766b4 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -9,7 +9,9 @@ widgetsinclude_HEADERS = \
e-account-manager.h \
e-account-tree-view.h \
e-action-combo-box.h \
+ e-activity-bar.h \
e-activity-proxy.h \
+ e-alert-bar.h \
e-attachment.h \
e-attachment-button.h \
e-attachment-dialog.h \
@@ -85,7 +87,9 @@ libemiscwidgets_la_SOURCES = \
e-account-manager.c \
e-account-tree-view.c \
e-action-combo-box.c \
+ e-activity-bar.c \
e-activity-proxy.c \
+ e-alert-bar.c \
e-attachment.c \
e-attachment-button.c \
e-attachment-dialog.c \
diff --git a/widgets/misc/e-activity-bar.c b/widgets/misc/e-activity-bar.c
new file mode 100644
index 0000000000..0337822adc
--- /dev/null
+++ b/widgets/misc/e-activity-bar.c
@@ -0,0 +1,351 @@
+/*
+ * e-activity-bar.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/>
+ *
+ */
+
+#include "e-activity-bar.h"
+
+#define E_ACTIVITY_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarPrivate))
+
+#define FEEDBACK_PERIOD 1 /* seconds */
+
+struct _EActivityBarPrivate {
+ EActivity *activity; /* weak reference */
+ GtkWidget *image; /* not referenced */
+ GtkWidget *label; /* not referenced */
+ GtkWidget *cancel; /* not referenced */
+
+ /* If the user clicks the Cancel button, keep the cancelled
+ * EActivity object alive for a short duration so the user
+ * gets some visual feedback that cancellation worked. */
+ guint timeout_id;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVITY
+};
+
+G_DEFINE_TYPE (
+ EActivityBar,
+ e_activity_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+activity_bar_update (EActivityBar *bar)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+ gboolean cancelled;
+ gboolean completed;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *description;
+
+ activity = e_activity_bar_get_activity (bar);
+
+ if (activity == NULL) {
+ gtk_widget_hide (GTK_WIDGET (bar));
+ return;
+ }
+
+ cancellable = e_activity_get_cancellable (activity);
+ cancelled = g_cancellable_is_cancelled (cancellable);
+ completed = e_activity_is_completed (activity);
+ icon_name = e_activity_get_icon_name (activity);
+
+ description = e_activity_describe (activity);
+ gtk_label_set_text (GTK_LABEL (bar->priv->label), description);
+
+ if (cancelled) {
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ attr_list = pango_attr_list_new ();
+
+ attr = pango_attr_strikethrough_new (TRUE);
+ pango_attr_list_insert (attr_list, attr);
+
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), attr_list);
+
+ pango_attr_list_unref (attr_list);
+ } else
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), NULL);
+
+ if (cancelled)
+ gtk_image_set_from_stock (
+ GTK_IMAGE (bar->priv->image),
+ GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+ else {
+ if (completed)
+ icon_name = "emblem-default";
+ gtk_image_set_from_icon_name (
+ GTK_IMAGE (bar->priv->image),
+ icon_name, GTK_ICON_SIZE_BUTTON);
+ }
+
+ visible = (icon_name != NULL);
+ gtk_widget_set_visible (bar->priv->image, visible);
+
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (bar->priv->cancel, visible);
+
+ sensitive = !cancelled && !completed;
+ gtk_widget_set_sensitive (bar->priv->cancel, sensitive);
+
+ visible = (description != NULL && *description != '\0');
+ gtk_widget_set_visible (GTK_WIDGET (bar), visible);
+
+ g_free (description);
+}
+
+static void
+activity_bar_cancel (EActivityBar *bar)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ cancellable = e_activity_get_cancellable (activity);
+ g_cancellable_cancel (cancellable);
+}
+
+static void
+activity_bar_feedback (EActivityBar *bar)
+{
+ EActivity *activity;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (bar->priv->timeout_id > 0)
+ g_source_remove (bar->priv->timeout_id);
+
+ /* Hold a reference on the EActivity for a short
+ * period so the activity bar stays visible. */
+ bar->priv->timeout_id = g_timeout_add_seconds_full (
+ G_PRIORITY_LOW, FEEDBACK_PERIOD, (GSourceFunc) gtk_false,
+ g_object_ref (activity), (GDestroyNotify) g_object_unref);
+}
+
+static void
+activity_bar_weak_notify_cb (EActivityBar *bar,
+ GObject *where_the_object_was)
+{
+ bar->priv->activity = NULL;
+ e_activity_bar_set_activity (bar, NULL);
+}
+
+static void
+activity_bar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ e_activity_bar_set_activity (
+ E_ACTIVITY_BAR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ g_value_set_object (
+ value, e_activity_bar_get_activity (
+ E_ACTIVITY_BAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_dispose (GObject *object)
+{
+ EActivityBarPrivate *priv;
+
+ priv = E_ACTIVITY_BAR_GET_PRIVATE (object);
+
+ if (priv->timeout_id > 0) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ if (priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_weak_unref (
+ G_OBJECT (priv->activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, object);
+ priv->activity = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_activity_bar_parent_class)->dispose (object);
+}
+
+static void
+e_activity_bar_class_init (EActivityBarClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EActivityBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = activity_bar_set_property;
+ object_class->get_property = activity_bar_get_property;
+ object_class->dispose = activity_bar_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVITY,
+ g_param_spec_object (
+ "activity",
+ NULL,
+ NULL,
+ E_TYPE_ACTIVITY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_activity_bar_init (EActivityBar *bar)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ bar->priv = E_ACTIVITY_BAR_GET_PRIVATE (bar);
+
+ container = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->image = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ bar->priv->label = widget;
+ gtk_widget_show (widget);
+
+ /* This is only shown if the EActivity has a GCancellable. */
+ widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_info_bar_add_action_widget (
+ GTK_INFO_BAR (bar), widget, GTK_RESPONSE_CANCEL);
+ bar->priv->cancel = widget;
+ gtk_widget_hide (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (activity_bar_cancel), bar);
+}
+
+GtkWidget *
+e_activity_bar_new (void)
+{
+ return g_object_new (E_TYPE_ACTIVITY_BAR, NULL);
+}
+
+EActivity *
+e_activity_bar_get_activity (EActivityBar *bar)
+{
+ g_return_val_if_fail (E_IS_ACTIVITY_BAR (bar), NULL);
+
+ return bar->priv->activity;
+}
+
+void
+e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
+
+ if (activity != NULL) {
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+ g_object_weak_ref (
+ G_OBJECT (activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, bar);
+ }
+
+ if (bar->priv->timeout_id > 0) {
+ g_source_remove (bar->priv->timeout_id);
+ bar->priv->timeout_id = 0;
+ }
+
+ if (bar->priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ bar->priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, bar);
+ g_object_weak_unref (
+ G_OBJECT (bar->priv->activity),
+ (GWeakNotify) activity_bar_weak_notify_cb, bar);
+ }
+
+ bar->priv->activity = activity;
+
+ if (activity != NULL) {
+ g_signal_connect_swapped (
+ activity, "cancelled",
+ G_CALLBACK (activity_bar_feedback), bar);
+
+ g_signal_connect_swapped (
+ activity, "completed",
+ G_CALLBACK (activity_bar_feedback), bar);
+
+ g_signal_connect_swapped (
+ activity, "cancelled",
+ G_CALLBACK (activity_bar_update), bar);
+
+ g_signal_connect_swapped (
+ activity, "completed",
+ G_CALLBACK (activity_bar_update), bar);
+
+ g_signal_connect_swapped (
+ activity, "notify",
+ G_CALLBACK (activity_bar_update), bar);
+ }
+
+ activity_bar_update (bar);
+
+ g_object_notify (G_OBJECT (bar), "activity");
+}
diff --git a/widgets/misc/e-activity-bar.h b/widgets/misc/e-activity-bar.h
new file mode 100644
index 0000000000..24f56eca53
--- /dev/null
+++ b/widgets/misc/e-activity-bar.h
@@ -0,0 +1,67 @@
+/*
+ * e-activity-bar.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_ACTIVITY_BAR_H
+#define E_ACTIVITY_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACTIVITY_BAR \
+ (e_activity_bar_get_type ())
+#define E_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBar))
+#define E_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+#define E_IS_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR))
+#define E_IS_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ACTIVITY_BAR))
+#define E_ACTIVITY_BAR_GET_CLASS(obj) \
+ (G_TYPE_CHECK_INSTANCE_GET_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EActivityBar EActivityBar;
+typedef struct _EActivityBarClass EActivityBarClass;
+typedef struct _EActivityBarPrivate EActivityBarPrivate;
+
+struct _EActivityBar {
+ GtkInfoBar parent;
+ EActivityBarPrivate *priv;
+};
+
+struct _EActivityBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_activity_bar_get_type (void);
+GtkWidget * e_activity_bar_new (void);
+EActivity * e_activity_bar_get_activity (EActivityBar *bar);
+void e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_ACTIVITY_BAR_H */
diff --git a/widgets/misc/e-activity-proxy.c b/widgets/misc/e-activity-proxy.c
index e96f18ed63..26d5d30ba0 100644
--- a/widgets/misc/e-activity-proxy.c
+++ b/widgets/misc/e-activity-proxy.c
@@ -69,6 +69,7 @@ activity_proxy_update (EActivityProxy *proxy)
gboolean clickable;
gboolean completed;
gboolean sensitive;
+ gboolean visible;
gchar *description;
activity = proxy->priv->activity;
@@ -108,10 +109,8 @@ activity_proxy_update (EActivityProxy *proxy)
gtk_widget_hide (proxy->priv->image);
}
- if (cancellable != NULL)
- gtk_widget_show (proxy->priv->cancel);
- else
- gtk_widget_hide (proxy->priv->cancel);
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (proxy->priv->cancel, visible);
sensitive = !(cancelled || completed);
gtk_widget_set_sensitive (proxy->priv->cancel, sensitive);
diff --git a/widgets/misc/e-alert-bar.c b/widgets/misc/e-alert-bar.c
new file mode 100644
index 0000000000..7796482689
--- /dev/null
+++ b/widgets/misc/e-alert-bar.c
@@ -0,0 +1,235 @@
+/*
+ * e-alert-bar.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/>
+ *
+ */
+
+#include "e-alert-bar.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#define E_ALERT_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
+
+#define ICON_SIZE GTK_ICON_SIZE_DIALOG
+
+struct _EAlertBarPrivate {
+ GQueue alerts;
+ GtkWidget *image; /* not referenced */
+ GtkWidget *primary_label; /* not referenced */
+ GtkWidget *secondary_label; /* not referenced */
+};
+
+G_DEFINE_TYPE (
+ EAlertBar,
+ e_alert_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+alert_bar_show_alert (EAlertBar *alert_bar)
+{
+ GtkImage *image;
+ GtkLabel *label;
+ GtkInfoBar *info_bar;
+ GtkWidget *action_area;
+ EAlertButton *buttons;
+ EAlert *alert;
+ GList *children;
+ GtkMessageType message_type;
+ const gchar *stock_id;
+ const gchar *text;
+ gint response_id;
+
+ info_bar = GTK_INFO_BAR (alert_bar);
+ action_area = gtk_info_bar_get_action_area (info_bar);
+
+ alert = g_queue_peek_head (&alert_bar->priv->alerts);
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ /* Remove all buttons from the previous alert. */
+ children = gtk_container_get_children (GTK_CONTAINER (action_area));
+ while (children != NULL) {
+ GtkWidget *child = GTK_WIDGET (children->data);
+ gtk_container_remove (GTK_CONTAINER (action_area), child);
+ children = g_list_delete_link (children, children);
+ }
+
+ /* Add new buttons. */
+ buttons = e_alert_peek_buttons (alert);
+ if (buttons == NULL) {
+ gtk_info_bar_add_button (
+ info_bar, _("_Dismiss"), GTK_RESPONSE_CLOSE);
+ } else while (buttons != NULL) {
+ const gchar *button_text;
+
+ if (buttons->stock != NULL)
+ button_text = buttons->stock;
+ else
+ button_text = buttons->label;
+
+ gtk_info_bar_add_button (
+ info_bar, button_text, buttons->response);
+
+ buttons = buttons->next;
+ }
+
+ response_id = e_alert_get_default_response (alert);
+ gtk_info_bar_set_default_response (info_bar, response_id);
+
+ message_type = e_alert_get_message_type (alert);
+ gtk_info_bar_set_message_type (info_bar, message_type);
+
+ text = e_alert_get_primary_text (alert);
+ label = GTK_LABEL (alert_bar->priv->primary_label);
+ gtk_label_set_text (label, text);
+
+ text = e_alert_get_secondary_text (alert);
+ label = GTK_LABEL (alert_bar->priv->secondary_label);
+ gtk_label_set_text (label, text);
+
+ stock_id = e_alert_get_stock_id (alert);
+ image = GTK_IMAGE (alert_bar->priv->image);
+ gtk_image_set_from_stock (image, stock_id, ICON_SIZE);
+
+ gtk_widget_show (GTK_WIDGET (alert_bar));
+}
+
+static void
+alert_bar_dispose (GObject *object)
+{
+ EAlertBarPrivate *priv;
+
+ priv = E_ALERT_BAR_GET_PRIVATE (object);
+
+ while (!g_queue_is_empty (&priv->alerts))
+ g_object_unref (g_queue_pop_head (&priv->alerts));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object);
+}
+
+static void
+alert_bar_response (GtkInfoBar *info_bar,
+ gint response_id)
+{
+ EAlertBar *alert_bar;
+ EAlert *alert;
+
+ alert_bar = E_ALERT_BAR (info_bar);
+
+ alert = g_queue_pop_head (&alert_bar->priv->alerts);
+ e_alert_response (alert, response_id);
+ g_object_unref (alert);
+
+ if (!g_queue_is_empty (&alert_bar->priv->alerts))
+ alert_bar_show_alert (alert_bar);
+ else
+ gtk_widget_hide (GTK_WIDGET (alert_bar));
+}
+
+static void
+e_alert_bar_class_init (EAlertBarClass *class)
+{
+ GObjectClass *object_class;
+ GtkInfoBarClass *info_bar_class;
+
+ g_type_class_add_private (class, sizeof (EAlertBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = alert_bar_dispose;
+
+ info_bar_class = GTK_INFO_BAR_CLASS (class);
+ info_bar_class->response = alert_bar_response;
+}
+
+static void
+e_alert_bar_init (EAlertBar *alert_bar)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar);
+
+ g_queue_init (&alert_bar->priv->alerts);
+
+ container = gtk_info_bar_get_content_area (GTK_INFO_BAR (alert_bar));
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->image = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ attr_list = pango_attr_list_new ();
+ attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
+ pango_attr_list_insert (attr_list, attr);
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (attr_list, attr);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->primary_label = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->secondary_label = widget;
+ gtk_widget_show (widget);
+
+ pango_attr_list_unref (attr_list);
+}
+
+GtkWidget *
+e_alert_bar_new (void)
+{
+ return g_object_new (E_TYPE_ALERT_BAR, NULL);
+}
+
+void
+e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert)
+{
+ gboolean show_it_now;
+
+ g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ show_it_now = g_queue_is_empty (&alert_bar->priv->alerts);
+ g_queue_push_tail (&alert_bar->priv->alerts, g_object_ref (alert));
+
+ if (show_it_now)
+ alert_bar_show_alert (alert_bar);
+}
diff --git a/widgets/misc/e-alert-bar.h b/widgets/misc/e-alert-bar.h
new file mode 100644
index 0000000000..fc23dec8bf
--- /dev/null
+++ b/widgets/misc/e-alert-bar.h
@@ -0,0 +1,66 @@
+/*
+ * e-alert-bar.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_BAR_H
+#define E_ALERT_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-alert.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT_BAR \
+ (e_alert_bar_get_type ())
+#define E_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBar))
+#define E_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_BAR, EAlertBarClass))
+#define E_IS_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_BAR))
+#define E_IS_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_BAR))
+#define E_ALERT_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlertBar EAlertBar;
+typedef struct _EAlertBarClass EAlertBarClass;
+typedef struct _EAlertBarPrivate EAlertBarPrivate;
+
+struct _EAlertBar {
+ GtkInfoBar parent;
+ EAlertBarPrivate *priv;
+};
+
+struct _EAlertBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_alert_bar_get_type (void);
+GtkWidget * e_alert_bar_new (void);
+void e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert);
+
+G_END_DECLS
+
+#endif /* E_ALERT_BAR_H */