From 451179909849e4e4058180f095e6ae889d97b797 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 21 Nov 2011 18:51:00 -0500 Subject: EAlertBar: Add a close button to all alerts. Replaces the 'Dismiss' button. All alerts appearing in an alert bar should be dismissable (i.e. non-modal). For modal alerts use a dialog. --- widgets/misc/e-alert-bar.c | 218 ++++++++++++++++++++++++++++++--------------- widgets/misc/e-alert-bar.h | 1 + 2 files changed, 147 insertions(+), 72 deletions(-) (limited to 'widgets/misc') diff --git a/widgets/misc/e-alert-bar.c b/widgets/misc/e-alert-bar.c index 7ec731e90d..75bf55ff25 100644 --- a/widgets/misc/e-alert-bar.c +++ b/widgets/misc/e-alert-bar.c @@ -16,14 +16,15 @@ * */ -#ifdef HAVE_CONFIG_H -#include -#endif - #include "e-alert-bar.h" +#include #include +#define E_ALERT_BAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate)) + /* GTK_ICON_SIZE_DIALOG is a tad too big. */ #define ICON_SIZE GTK_ICON_SIZE_DND @@ -42,20 +43,31 @@ G_DEFINE_TYPE ( e_alert_bar, GTK_TYPE_INFO_BAR) +static void +alert_bar_response_close (EAlert *alert) +{ + e_alert_response (alert, GTK_RESPONSE_CLOSE); +} + static void alert_bar_show_alert (EAlertBar *alert_bar) { GtkImage *image; - GtkLabel *label; GtkInfoBar *info_bar; GtkWidget *action_area; + GtkWidget *widget; EAlert *alert; GList *actions; GList *children; GtkMessageType message_type; + const gchar *primary_text; + const gchar *secondary_text; const gchar *stock_id; - const gchar *text; + gboolean have_primary_text; + gboolean have_secondary_text; + gboolean visible; gint response_id; + gchar *markup; info_bar = GTK_INFO_BAR (alert_bar); action_area = gtk_info_bar_get_action_area (info_bar); @@ -71,48 +83,94 @@ alert_bar_show_alert (EAlertBar *alert_bar) children = g_list_delete_link (children, children); } - /* Add new buttons. */ + /* Add alert-specific buttons. */ actions = e_alert_peek_actions (alert); while (actions != NULL) { - GtkWidget *button; - /* These actions are already wired to trigger an * EAlert::response signal when activated, which * will in turn call gtk_info_bar_response(), so * we can add buttons directly to the action * area without knowning their response IDs. */ - button = gtk_button_new (); + widget = gtk_button_new (); gtk_activatable_set_related_action ( - GTK_ACTIVATABLE (button), + GTK_ACTIVATABLE (widget), GTK_ACTION (actions->data)); gtk_box_pack_end ( - GTK_BOX (action_area), - button, FALSE, FALSE, 0); + GTK_BOX (action_area), widget, FALSE, FALSE, 0); actions = g_list_next (actions); } + /* Add a dismiss button. */ + widget = gtk_button_new (); + gtk_button_set_image ( + GTK_BUTTON (widget), + gtk_image_new_from_stock ( + GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); + gtk_button_set_relief ( + GTK_BUTTON (widget), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text ( + widget, _("Close this message")); + gtk_box_pack_end ( + GTK_BOX (action_area), widget, FALSE, FALSE, 0); + gtk_button_box_set_child_non_homogeneous ( + GTK_BUTTON_BOX (action_area), widget, TRUE); + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "clicked", + G_CALLBACK (alert_bar_response_close), alert); + + primary_text = e_alert_get_primary_text (alert); + secondary_text = e_alert_get_secondary_text (alert); + + if (primary_text == NULL) + primary_text = ""; + + if (secondary_text == NULL) + secondary_text = ""; + + have_primary_text = (*primary_text != '\0'); + have_secondary_text = (*secondary_text != '\0'); + 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); + widget = alert_bar->priv->primary_label; + if (have_primary_text && have_secondary_text) + markup = g_markup_printf_escaped ( + "%s", primary_text); + else + markup = g_markup_escape_text (primary_text, -1); + gtk_label_set_markup (GTK_LABEL (widget), markup); + gtk_widget_set_visible (widget, have_primary_text); + g_free (markup); + + widget = alert_bar->priv->secondary_label; + if (have_primary_text && have_secondary_text) + markup = g_markup_printf_escaped ( + "%s", secondary_text); + else + markup = g_markup_escape_text (secondary_text, -1); + gtk_label_set_markup (GTK_LABEL (widget), markup); + gtk_widget_set_visible (widget, have_secondary_text); + g_free (markup); 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); + /* Avoid showing an image for one-line alerts, + * which are usually questions or informational. */ + visible = have_primary_text && have_secondary_text; + gtk_widget_set_visible (alert_bar->priv->image, visible); + gtk_widget_show (GTK_WIDGET (alert_bar)); /* Warnings are generally meant for transient errors. @@ -130,7 +188,6 @@ alert_bar_response_cb (EAlert *alert, { GQueue *queue; EAlert *head; - GList *link; gboolean was_head; queue = &alert_bar->priv->alerts; @@ -140,12 +197,8 @@ alert_bar_response_cb (EAlert *alert, g_signal_handlers_disconnect_by_func ( alert, alert_bar_response_cb, alert_bar); - /* XXX Would be easier if g_queue_remove() returned a boolean. */ - link = g_queue_find (queue, alert); - if (link != NULL) { - g_queue_delete_link (queue, link); + if (g_queue_remove (queue, alert)) g_object_unref (alert); - } if (g_queue_is_empty (queue)) gtk_widget_hide (GTK_WIDGET (alert_bar)); @@ -161,7 +214,7 @@ alert_bar_dispose (GObject *object) { EAlertBarPrivate *priv; - priv = E_ALERT_BAR (object)->priv; + priv = E_ALERT_BAR_GET_PRIVATE (object); while (!g_queue_is_empty (&priv->alerts)) { EAlert *alert = g_queue_pop_head (&priv->alerts); @@ -174,48 +227,37 @@ alert_bar_dispose (GObject *object) G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object); } -static GtkSizeRequestMode -alert_bar_get_request_mode (GtkWidget *widget) -{ - /* GtkHBox does width-for-height by default. But we - * want the alert bar to be as short as possible. */ - return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; -} - static void -e_alert_bar_class_init (EAlertBarClass *class) +alert_bar_constructed (GObject *object) { - GObjectClass *object_class; - GtkWidgetClass *widget_class; + EAlertBarPrivate *priv; + GtkInfoBar *info_bar; + GtkWidget *action_area; + GtkWidget *content_area; + GtkWidget *container; + GtkWidget *widget; - g_type_class_add_private (class, sizeof (EAlertBarPrivate)); + priv = E_ALERT_BAR_GET_PRIVATE (object); - object_class = G_OBJECT_CLASS (class); - object_class->dispose = alert_bar_dispose; + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_alert_bar_parent_class)->constructed (object); - widget_class = GTK_WIDGET_CLASS (class); - widget_class->get_request_mode = alert_bar_get_request_mode; -} + g_queue_init (&priv->alerts); -static void -e_alert_bar_init (EAlertBar *alert_bar) -{ - GtkWidget *container; - GtkWidget *widget; - PangoAttribute *attr; - PangoAttrList *attr_list; - - alert_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - alert_bar, E_TYPE_ALERT_BAR, EAlertBarPrivate); + info_bar = GTK_INFO_BAR (object); + action_area = gtk_info_bar_get_action_area (info_bar); + content_area = gtk_info_bar_get_content_area (info_bar); - g_queue_init (&alert_bar->priv->alerts); + gtk_orientable_set_orientation ( + GTK_ORIENTABLE (action_area), GTK_ORIENTATION_HORIZONTAL); + gtk_widget_set_valign (action_area, GTK_ALIGN_START); - container = gtk_info_bar_get_content_area (GTK_INFO_BAR (alert_bar)); + container = content_area; 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; + priv->image = widget; gtk_widget_show (widget); widget = gtk_vbox_new (FALSE, 12); @@ -224,35 +266,53 @@ e_alert_bar_init (EAlertBar *alert_bar) container = widget; - attr_list = pango_attr_list_new (); - 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_label_set_selectable (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; + priv->primary_label = widget; gtk_widget_show (widget); - pango_attr_list_unref (attr_list); - - attr_list = pango_attr_list_new (); - attr = pango_attr_scale_new (PANGO_SCALE_SMALL); - 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_label_set_selectable (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; + priv->secondary_label = widget; gtk_widget_show (widget); - pango_attr_list_unref (attr_list); + container = action_area; +} + +static GtkSizeRequestMode +alert_bar_get_request_mode (GtkWidget *widget) +{ + /* GtkHBox does width-for-height by default. But we + * want the alert bar to be as short as possible. */ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +e_alert_bar_class_init (EAlertBarClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (EAlertBarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = alert_bar_dispose; + object_class->constructed = alert_bar_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->get_request_mode = alert_bar_get_request_mode; +} + +static void +e_alert_bar_init (EAlertBar *alert_bar) +{ + alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar); } GtkWidget * @@ -261,6 +321,20 @@ e_alert_bar_new (void) return g_object_new (E_TYPE_ALERT_BAR, NULL); } +void +e_alert_bar_clear (EAlertBar *alert_bar) +{ + GQueue *queue; + EAlert *alert; + + g_return_if_fail (E_IS_ALERT_BAR (alert_bar)); + + queue = &alert_bar->priv->alerts; + + while ((alert = g_queue_pop_head (queue)) != NULL) + alert_bar_response_close (alert); +} + typedef struct { gboolean found; EAlert *looking_for; diff --git a/widgets/misc/e-alert-bar.h b/widgets/misc/e-alert-bar.h index fc23dec8bf..f1e84b5016 100644 --- a/widgets/misc/e-alert-bar.h +++ b/widgets/misc/e-alert-bar.h @@ -58,6 +58,7 @@ struct _EAlertBarClass { GType e_alert_bar_get_type (void); GtkWidget * e_alert_bar_new (void); +void e_alert_bar_clear (EAlertBar *alert_bar); void e_alert_bar_add_alert (EAlertBar *alert_bar, EAlert *alert); -- cgit v1.2.3