aboutsummaryrefslogblamecommitdiffstats
path: root/widgets/misc/e-alert-bar.c
blob: 7ec731e90da7144621d759b27963360ec0802952 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                             



                    

                        

                           
                                            



                                                     



















                                                            
                      
                       




















                                                                            




















                                                                




















                                                                  






                                                                     


           


                                            
 

















                                                                         
 






                                                                


           
                                   
 
                               
 
                                          
 





                                                                 
 

                                                                    

 







                                                            



                                              
                                     




                                                                    


                                                                    









                                       

                                                               




                                                                             












                                                                          





                                                                 
                                                            




                                                                          





                                                         
                                      
                                                                 
                                                           
                                                            













                                                                          
                

                            
                

           

                                               
 

                                                   






                                                                    

 



                                            
                         
 


                                                      


                               


                                                          



                       



                                                               
                                                                           
 
                                         
 
/*
 * 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/>
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "e-alert-bar.h"

#include <glib/gi18n-lib.h>

/* GTK_ICON_SIZE_DIALOG is a tad too big. */
#define ICON_SIZE GTK_ICON_SIZE_DND

/* Dismiss warnings automatically after 5 minutes. */
#define WARNING_TIMEOUT_SECONDS (5 * 60)

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;
    EAlert *alert;
    GList *actions;
    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. */
    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 ();

        gtk_activatable_set_related_action (
            GTK_ACTIVATABLE (button),
            GTK_ACTION (actions->data));

        gtk_box_pack_end (
            GTK_BOX (action_area),
            button, FALSE, FALSE, 0);

        actions = g_list_next (actions);
    }

    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));

    /* Warnings are generally meant for transient errors.
     * No need to leave them up indefinitely.  Close them
     * automatically if the user hasn't responded after a
     * reasonable period of time has elapsed. */
    if (message_type == GTK_MESSAGE_WARNING)
        e_alert_start_timer (alert, WARNING_TIMEOUT_SECONDS);
}

static void
alert_bar_response_cb (EAlert *alert,
                       gint response_id,
                       EAlertBar *alert_bar)
{
    GQueue *queue;
    EAlert *head;
    GList *link;
    gboolean was_head;

    queue = &alert_bar->priv->alerts;
    head = g_queue_peek_head (queue);
    was_head = (alert == head);

    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);
        g_object_unref (alert);
    }

    if (g_queue_is_empty (queue))
        gtk_widget_hide (GTK_WIDGET (alert_bar));
    else if (was_head) {
        GtkInfoBar *info_bar = GTK_INFO_BAR (alert_bar);
        gtk_info_bar_response (info_bar, response_id);
        alert_bar_show_alert (alert_bar);
    }
}

static void
alert_bar_dispose (GObject *object)
{
    EAlertBarPrivate *priv;

    priv = E_ALERT_BAR (object)->priv;

    while (!g_queue_is_empty (&priv->alerts)) {
        EAlert *alert = g_queue_pop_head (&priv->alerts);
        g_signal_handlers_disconnect_by_func (
            alert, alert_bar_response_cb, object);
        g_object_unref (alert);
    }

    /* Chain up to parent's dispose() method. */
    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)
{
    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;

    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)
{
    GtkWidget *container;
    GtkWidget *widget;
    PangoAttribute *attr;
    PangoAttrList *attr_list;

    alert_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (
        alert_bar, E_TYPE_ALERT_BAR, EAlertBarPrivate);

    g_queue_init (&alert_bar->priv->alerts);

    container = gtk_info_bar_get_content_area (GTK_INFO_BAR (alert_bar));

    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_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;
    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;
    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);
}

typedef struct {
    gboolean found;
    EAlert *looking_for;
} DuplicateData;

static void
alert_bar_find_duplicate_cb (EAlert *alert,
                             DuplicateData *dd)
{
    g_return_if_fail (dd->looking_for != NULL);

    dd->found |= (
        e_alert_get_message_type (alert) ==
        e_alert_get_message_type (dd->looking_for) &&
        g_strcmp0 (e_alert_get_primary_text (alert),
        e_alert_get_primary_text (dd->looking_for)) == 0 &&
        g_strcmp0 (e_alert_get_secondary_text (alert),
        e_alert_get_secondary_text (dd->looking_for)) == 0);
}

void
e_alert_bar_add_alert (EAlertBar *alert_bar,
                       EAlert *alert)
{
    DuplicateData dd;

    g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
    g_return_if_fail (E_IS_ALERT (alert));

    dd.found = FALSE;
    dd.looking_for = alert;

    g_queue_foreach (
        &alert_bar->priv->alerts,
        (GFunc) alert_bar_find_duplicate_cb, &dd);

    if (dd.found)
        return;

    g_signal_connect (
        alert, "response",
        G_CALLBACK (alert_bar_response_cb), alert_bar);

    g_queue_push_head (&alert_bar->priv->alerts, g_object_ref (alert));

    alert_bar_show_alert (alert_bar);
}